config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

commit c58b1b135685592c85689df372285a88b6f1a7cd
parent 697ef4fa682ecdd917be856f92b7b6927f198403
Author: dwrz <dwrz@dwrz.net>
Date:   Sun,  9 Jun 2024 16:33:53 +0000

Update Emacs packages

Diffstat:
Memacs/elpa/archives/gnu/archive-contents | 24++++++++++++------------
Memacs/elpa/archives/gnu/archive-contents.signed | 4++--
Memacs/elpa/archives/melpa/archive-contents | 215++++++++++++++++++++++++++++++++++++++++---------------------------------------
Memacs/elpa/archives/nongnu/archive-contents | 18+++++++++++++++---
Memacs/elpa/archives/nongnu/archive-contents.signed | 4++--
Demacs/elpa/doom-modeline-20240510.144/doom-modeline-pkg.el | 17-----------------
Demacs/elpa/doom-modeline-20240510.144/doom-modeline-segments.el | 3218-------------------------------------------------------------------------------
Demacs/elpa/doom-modeline-20240510.144/doom-modeline-segments.elc | 0
Remacs/elpa/doom-modeline-20240510.144/doom-modeline-autoloads.el -> emacs/elpa/doom-modeline-20240605.628/doom-modeline-autoloads.el | 0
Remacs/elpa/doom-modeline-20240510.144/doom-modeline-core.el -> emacs/elpa/doom-modeline-20240605.628/doom-modeline-core.el | 0
Remacs/elpa/doom-modeline-20240510.144/doom-modeline-core.elc -> emacs/elpa/doom-modeline-20240605.628/doom-modeline-core.elc | 0
Remacs/elpa/doom-modeline-20240510.144/doom-modeline-env.el -> emacs/elpa/doom-modeline-20240605.628/doom-modeline-env.el | 0
Remacs/elpa/doom-modeline-20240510.144/doom-modeline-env.elc -> emacs/elpa/doom-modeline-20240605.628/doom-modeline-env.elc | 0
Aemacs/elpa/doom-modeline-20240605.628/doom-modeline-pkg.el | 17+++++++++++++++++
Aemacs/elpa/doom-modeline-20240605.628/doom-modeline-segments.el | 3243+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aemacs/elpa/doom-modeline-20240605.628/doom-modeline-segments.elc | 0
Remacs/elpa/doom-modeline-20240510.144/doom-modeline.el -> emacs/elpa/doom-modeline-20240605.628/doom-modeline.el | 0
Remacs/elpa/doom-modeline-20240510.144/doom-modeline.elc -> emacs/elpa/doom-modeline-20240605.628/doom-modeline.elc | 0
Demacs/elpa/embark-20240419.452/embark-pkg.el | 15---------------
Demacs/elpa/embark-20240419.452/embark.el | 4604-------------------------------------------------------------------------------
Demacs/elpa/embark-20240419.452/embark.elc | 0
Remacs/elpa/embark-20240419.452/dir -> emacs/elpa/embark-20240607.2213/dir | 0
Remacs/elpa/embark-20240419.452/embark-autoloads.el -> emacs/elpa/embark-20240607.2213/embark-autoloads.el | 0
Remacs/elpa/embark-20240419.452/embark-org.el -> emacs/elpa/embark-20240607.2213/embark-org.el | 0
Remacs/elpa/embark-20240419.452/embark-org.elc -> emacs/elpa/embark-20240607.2213/embark-org.elc | 0
Aemacs/elpa/embark-20240607.2213/embark-pkg.el | 15+++++++++++++++
Aemacs/elpa/embark-20240607.2213/embark.el | 4604+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aemacs/elpa/embark-20240607.2213/embark.elc | 0
Remacs/elpa/embark-20240419.452/embark.info -> emacs/elpa/embark-20240607.2213/embark.info | 0
Demacs/elpa/magit-20240522.204/magit-diff.elc | 0
Demacs/elpa/magit-20240522.204/magit-pkg.el | 20--------------------
Demacs/elpa/magit-20240522.204/magit-transient.elc | 0
Demacs/elpa/magit-20240522.204/magit.info | 11595-------------------------------------------------------------------------------
Remacs/elpa/magit-20240522.204/AUTHORS.md -> emacs/elpa/magit-20240605.1500/AUTHORS.md | 0
Remacs/elpa/magit-20240522.204/LICENSE -> emacs/elpa/magit-20240605.1500/LICENSE | 0
Remacs/elpa/magit-20240522.204/dir -> emacs/elpa/magit-20240605.1500/dir | 0
Remacs/elpa/magit-20240522.204/git-rebase.el -> emacs/elpa/magit-20240605.1500/git-rebase.el | 0
Remacs/elpa/magit-20240522.204/git-rebase.elc -> emacs/elpa/magit-20240605.1500/git-rebase.elc | 0
Remacs/elpa/magit-20240522.204/magit-apply.el -> emacs/elpa/magit-20240605.1500/magit-apply.el | 0
Remacs/elpa/magit-20240522.204/magit-apply.elc -> emacs/elpa/magit-20240605.1500/magit-apply.elc | 0
Remacs/elpa/magit-20240522.204/magit-autoloads.el -> emacs/elpa/magit-20240605.1500/magit-autoloads.el | 0
Remacs/elpa/magit-20240522.204/magit-autorevert.el -> emacs/elpa/magit-20240605.1500/magit-autorevert.el | 0
Remacs/elpa/magit-20240522.204/magit-autorevert.elc -> emacs/elpa/magit-20240605.1500/magit-autorevert.elc | 0
Remacs/elpa/magit-20240522.204/magit-base.el -> emacs/elpa/magit-20240605.1500/magit-base.el | 0
Remacs/elpa/magit-20240522.204/magit-base.elc -> emacs/elpa/magit-20240605.1500/magit-base.elc | 0
Remacs/elpa/magit-20240522.204/magit-bisect.el -> emacs/elpa/magit-20240605.1500/magit-bisect.el | 0
Remacs/elpa/magit-20240522.204/magit-bisect.elc -> emacs/elpa/magit-20240605.1500/magit-bisect.elc | 0
Remacs/elpa/magit-20240522.204/magit-blame.el -> emacs/elpa/magit-20240605.1500/magit-blame.el | 0
Remacs/elpa/magit-20240522.204/magit-blame.elc -> emacs/elpa/magit-20240605.1500/magit-blame.elc | 0
Remacs/elpa/magit-20240522.204/magit-bookmark.el -> emacs/elpa/magit-20240605.1500/magit-bookmark.el | 0
Remacs/elpa/magit-20240522.204/magit-bookmark.elc -> emacs/elpa/magit-20240605.1500/magit-bookmark.elc | 0
Remacs/elpa/magit-20240522.204/magit-branch.el -> emacs/elpa/magit-20240605.1500/magit-branch.el | 0
Remacs/elpa/magit-20240522.204/magit-branch.elc -> emacs/elpa/magit-20240605.1500/magit-branch.elc | 0
Remacs/elpa/magit-20240522.204/magit-bundle.el -> emacs/elpa/magit-20240605.1500/magit-bundle.el | 0
Remacs/elpa/magit-20240522.204/magit-bundle.elc -> emacs/elpa/magit-20240605.1500/magit-bundle.elc | 0
Remacs/elpa/magit-20240522.204/magit-clone.el -> emacs/elpa/magit-20240605.1500/magit-clone.el | 0
Remacs/elpa/magit-20240522.204/magit-clone.elc -> emacs/elpa/magit-20240605.1500/magit-clone.elc | 0
Remacs/elpa/magit-20240522.204/magit-commit.el -> emacs/elpa/magit-20240605.1500/magit-commit.el | 0
Remacs/elpa/magit-20240522.204/magit-commit.elc -> emacs/elpa/magit-20240605.1500/magit-commit.elc | 0
Remacs/elpa/magit-20240522.204/magit-core.el -> emacs/elpa/magit-20240605.1500/magit-core.el | 0
Remacs/elpa/magit-20240522.204/magit-core.elc -> emacs/elpa/magit-20240605.1500/magit-core.elc | 0
Remacs/elpa/magit-20240522.204/magit-diff.el -> emacs/elpa/magit-20240605.1500/magit-diff.el | 0
Aemacs/elpa/magit-20240605.1500/magit-diff.elc | 0
Remacs/elpa/magit-20240522.204/magit-ediff.el -> emacs/elpa/magit-20240605.1500/magit-ediff.el | 0
Remacs/elpa/magit-20240522.204/magit-ediff.elc -> emacs/elpa/magit-20240605.1500/magit-ediff.elc | 0
Remacs/elpa/magit-20240522.204/magit-extras.el -> emacs/elpa/magit-20240605.1500/magit-extras.el | 0
Remacs/elpa/magit-20240522.204/magit-extras.elc -> emacs/elpa/magit-20240605.1500/magit-extras.elc | 0
Remacs/elpa/magit-20240522.204/magit-fetch.el -> emacs/elpa/magit-20240605.1500/magit-fetch.el | 0
Remacs/elpa/magit-20240522.204/magit-fetch.elc -> emacs/elpa/magit-20240605.1500/magit-fetch.elc | 0
Remacs/elpa/magit-20240522.204/magit-files.el -> emacs/elpa/magit-20240605.1500/magit-files.el | 0
Remacs/elpa/magit-20240522.204/magit-files.elc -> emacs/elpa/magit-20240605.1500/magit-files.elc | 0
Remacs/elpa/magit-20240522.204/magit-git.el -> emacs/elpa/magit-20240605.1500/magit-git.el | 0
Remacs/elpa/magit-20240522.204/magit-git.elc -> emacs/elpa/magit-20240605.1500/magit-git.elc | 0
Remacs/elpa/magit-20240522.204/magit-gitignore.el -> emacs/elpa/magit-20240605.1500/magit-gitignore.el | 0
Remacs/elpa/magit-20240522.204/magit-gitignore.elc -> emacs/elpa/magit-20240605.1500/magit-gitignore.elc | 0
Remacs/elpa/magit-20240522.204/magit-log.el -> emacs/elpa/magit-20240605.1500/magit-log.el | 0
Remacs/elpa/magit-20240522.204/magit-log.elc -> emacs/elpa/magit-20240605.1500/magit-log.elc | 0
Remacs/elpa/magit-20240522.204/magit-margin.el -> emacs/elpa/magit-20240605.1500/magit-margin.el | 0
Remacs/elpa/magit-20240522.204/magit-margin.elc -> emacs/elpa/magit-20240605.1500/magit-margin.elc | 0
Remacs/elpa/magit-20240522.204/magit-merge.el -> emacs/elpa/magit-20240605.1500/magit-merge.el | 0
Remacs/elpa/magit-20240522.204/magit-merge.elc -> emacs/elpa/magit-20240605.1500/magit-merge.elc | 0
Remacs/elpa/magit-20240522.204/magit-mode.el -> emacs/elpa/magit-20240605.1500/magit-mode.el | 0
Remacs/elpa/magit-20240522.204/magit-mode.elc -> emacs/elpa/magit-20240605.1500/magit-mode.elc | 0
Remacs/elpa/magit-20240522.204/magit-notes.el -> emacs/elpa/magit-20240605.1500/magit-notes.el | 0
Remacs/elpa/magit-20240522.204/magit-notes.elc -> emacs/elpa/magit-20240605.1500/magit-notes.elc | 0
Remacs/elpa/magit-20240522.204/magit-patch.el -> emacs/elpa/magit-20240605.1500/magit-patch.el | 0
Remacs/elpa/magit-20240522.204/magit-patch.elc -> emacs/elpa/magit-20240605.1500/magit-patch.elc | 0
Aemacs/elpa/magit-20240605.1500/magit-pkg.el | 20++++++++++++++++++++
Remacs/elpa/magit-20240522.204/magit-process.el -> emacs/elpa/magit-20240605.1500/magit-process.el | 0
Remacs/elpa/magit-20240522.204/magit-process.elc -> emacs/elpa/magit-20240605.1500/magit-process.elc | 0
Remacs/elpa/magit-20240522.204/magit-pull.el -> emacs/elpa/magit-20240605.1500/magit-pull.el | 0
Remacs/elpa/magit-20240522.204/magit-pull.elc -> emacs/elpa/magit-20240605.1500/magit-pull.elc | 0
Remacs/elpa/magit-20240522.204/magit-push.el -> emacs/elpa/magit-20240605.1500/magit-push.el | 0
Remacs/elpa/magit-20240522.204/magit-push.elc -> emacs/elpa/magit-20240605.1500/magit-push.elc | 0
Remacs/elpa/magit-20240522.204/magit-reflog.el -> emacs/elpa/magit-20240605.1500/magit-reflog.el | 0
Remacs/elpa/magit-20240522.204/magit-reflog.elc -> emacs/elpa/magit-20240605.1500/magit-reflog.elc | 0
Remacs/elpa/magit-20240522.204/magit-refs.el -> emacs/elpa/magit-20240605.1500/magit-refs.el | 0
Remacs/elpa/magit-20240522.204/magit-refs.elc -> emacs/elpa/magit-20240605.1500/magit-refs.elc | 0
Remacs/elpa/magit-20240522.204/magit-remote.el -> emacs/elpa/magit-20240605.1500/magit-remote.el | 0
Remacs/elpa/magit-20240522.204/magit-remote.elc -> emacs/elpa/magit-20240605.1500/magit-remote.elc | 0
Remacs/elpa/magit-20240522.204/magit-repos.el -> emacs/elpa/magit-20240605.1500/magit-repos.el | 0
Remacs/elpa/magit-20240522.204/magit-repos.elc -> emacs/elpa/magit-20240605.1500/magit-repos.elc | 0
Remacs/elpa/magit-20240522.204/magit-reset.el -> emacs/elpa/magit-20240605.1500/magit-reset.el | 0
Remacs/elpa/magit-20240522.204/magit-reset.elc -> emacs/elpa/magit-20240605.1500/magit-reset.elc | 0
Remacs/elpa/magit-20240522.204/magit-sequence.el -> emacs/elpa/magit-20240605.1500/magit-sequence.el | 0
Remacs/elpa/magit-20240522.204/magit-sequence.elc -> emacs/elpa/magit-20240605.1500/magit-sequence.elc | 0
Remacs/elpa/magit-20240522.204/magit-sparse-checkout.el -> emacs/elpa/magit-20240605.1500/magit-sparse-checkout.el | 0
Remacs/elpa/magit-20240522.204/magit-sparse-checkout.elc -> emacs/elpa/magit-20240605.1500/magit-sparse-checkout.elc | 0
Remacs/elpa/magit-20240522.204/magit-stash.el -> emacs/elpa/magit-20240605.1500/magit-stash.el | 0
Remacs/elpa/magit-20240522.204/magit-stash.elc -> emacs/elpa/magit-20240605.1500/magit-stash.elc | 0
Remacs/elpa/magit-20240522.204/magit-status.el -> emacs/elpa/magit-20240605.1500/magit-status.el | 0
Remacs/elpa/magit-20240522.204/magit-status.elc -> emacs/elpa/magit-20240605.1500/magit-status.elc | 0
Remacs/elpa/magit-20240522.204/magit-submodule.el -> emacs/elpa/magit-20240605.1500/magit-submodule.el | 0
Remacs/elpa/magit-20240522.204/magit-submodule.elc -> emacs/elpa/magit-20240605.1500/magit-submodule.elc | 0
Remacs/elpa/magit-20240522.204/magit-subtree.el -> emacs/elpa/magit-20240605.1500/magit-subtree.el | 0
Remacs/elpa/magit-20240522.204/magit-subtree.elc -> emacs/elpa/magit-20240605.1500/magit-subtree.elc | 0
Remacs/elpa/magit-20240522.204/magit-tag.el -> emacs/elpa/magit-20240605.1500/magit-tag.el | 0
Remacs/elpa/magit-20240522.204/magit-tag.elc -> emacs/elpa/magit-20240605.1500/magit-tag.elc | 0
Remacs/elpa/magit-20240522.204/magit-transient.el -> emacs/elpa/magit-20240605.1500/magit-transient.el | 0
Aemacs/elpa/magit-20240605.1500/magit-transient.elc | 0
Remacs/elpa/magit-20240522.204/magit-wip.el -> emacs/elpa/magit-20240605.1500/magit-wip.el | 0
Remacs/elpa/magit-20240522.204/magit-wip.elc -> emacs/elpa/magit-20240605.1500/magit-wip.elc | 0
Remacs/elpa/magit-20240522.204/magit-worktree.el -> emacs/elpa/magit-20240605.1500/magit-worktree.el | 0
Remacs/elpa/magit-20240522.204/magit-worktree.elc -> emacs/elpa/magit-20240605.1500/magit-worktree.elc | 0
Remacs/elpa/magit-20240522.204/magit.el -> emacs/elpa/magit-20240605.1500/magit.el | 0
Remacs/elpa/magit-20240522.204/magit.elc -> emacs/elpa/magit-20240605.1500/magit.elc | 0
Aemacs/elpa/magit-20240605.1500/magit.info | 11592+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Demacs/elpa/orderless-20240401.959/orderless-kwd.elc | 0
Demacs/elpa/orderless-20240401.959/orderless-pkg.el | 14--------------
Demacs/elpa/orderless-20240401.959/orderless.el | 661-------------------------------------------------------------------------------
Demacs/elpa/orderless-20240401.959/orderless.elc | 0
Remacs/elpa/orderless-20240401.959/dir -> emacs/elpa/orderless-20240606.1026/dir | 0
Remacs/elpa/orderless-20240401.959/orderless-autoloads.el -> emacs/elpa/orderless-20240606.1026/orderless-autoloads.el | 0
Remacs/elpa/orderless-20240401.959/orderless-kwd.el -> emacs/elpa/orderless-20240606.1026/orderless-kwd.el | 0
Aemacs/elpa/orderless-20240606.1026/orderless-kwd.elc | 0
Aemacs/elpa/orderless-20240606.1026/orderless-pkg.el | 14++++++++++++++
Aemacs/elpa/orderless-20240606.1026/orderless.el | 663+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aemacs/elpa/orderless-20240606.1026/orderless.elc | 0
Remacs/elpa/orderless-20240401.959/orderless.info -> emacs/elpa/orderless-20240606.1026/orderless.info | 0
Demacs/elpa/transient-20240525.1118/transient-pkg.el | 16----------------
Demacs/elpa/transient-20240525.1118/transient.el | 4508-------------------------------------------------------------------------------
Demacs/elpa/transient-20240525.1118/transient.elc | 0
Demacs/elpa/transient-20240525.1118/transient.info | 3260-------------------------------------------------------------------------------
Remacs/elpa/transient-20240525.1118/dir -> emacs/elpa/transient-20240607.1832/dir | 0
Remacs/elpa/transient-20240525.1118/gpl.info -> emacs/elpa/transient-20240607.1832/gpl.info | 0
Remacs/elpa/transient-20240525.1118/transient-autoloads.el -> emacs/elpa/transient-20240607.1832/transient-autoloads.el | 0
Aemacs/elpa/transient-20240607.1832/transient-pkg.el | 16++++++++++++++++
Aemacs/elpa/transient-20240607.1832/transient.el | 4508+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aemacs/elpa/transient-20240607.1832/transient.elc | 0
Aemacs/elpa/transient-20240607.1832/transient.info | 3273+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Demacs/elpa/with-editor-20240415.1558/with-editor-autoloads.el | 105-------------------------------------------------------------------------------
Demacs/elpa/with-editor-20240415.1558/with-editor-pkg.el | 15---------------
Demacs/elpa/with-editor-20240415.1558/with-editor.el | 984-------------------------------------------------------------------------------
Demacs/elpa/with-editor-20240415.1558/with-editor.elc | 0
Remacs/elpa/with-editor-20240415.1558/dir -> emacs/elpa/with-editor-20240609.1518/dir | 0
Aemacs/elpa/with-editor-20240609.1518/with-editor-autoloads.el | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aemacs/elpa/with-editor-20240609.1518/with-editor-pkg.el | 15+++++++++++++++
Aemacs/elpa/with-editor-20240609.1518/with-editor.el | 980+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aemacs/elpa/with-editor-20240609.1518/with-editor.elc | 0
Remacs/elpa/with-editor-20240415.1558/with-editor.info -> emacs/elpa/with-editor-20240609.1518/with-editor.info | 0
160 files changed, 29205 insertions(+), 29157 deletions(-)

diff --git a/emacs/elpa/archives/gnu/archive-contents b/emacs/elpa/archives/gnu/archive-contents @@ -1330,7 +1330,7 @@ (:url . "https://elpa.gnu.org/packages/elisp-benchmarks.html") (:commit . "1a3d97954957a95a179806e0d49ca6d178b097af"))]) (ellama . - [(0 9 5) + [(0 9 7) ((emacs (28 1)) (llm @@ -1343,7 +1343,7 @@ (:maintainer "Sergey Kostyaev" . "sskostyaev@gmail.com") (:authors ("Sergey Kostyaev" . "sskostyaev@gmail.com")) - (:commit . "1e1db6d5f1ced38ad1c76ffca1651787d54998de"))]) + (:commit . "361e1aa26541f2a8675fcda2c907fea4b68aff00"))]) (emacs-gc-stats . [(1 4 2) ((emacs @@ -1385,7 +1385,7 @@ ("Omar Antolín Camarena" . "omar@matem.unam.mx")) (:commit . "195add1f1ccd1059472c9df7334c97c4d155425e"))]) (ement . - [(0 15) + [(0 15 1) ((emacs (27 1)) (map @@ -1408,9 +1408,9 @@ (:maintainer "Adam Porter" . "adam@alphapapa.net") (:authors ("Adam Porter" . "adam@alphapapa.net")) - (:commit . "dc314a120ca6c62c49342e1b109ae1282116257a"))]) + (:commit . "e120fdb3bdf23e31f6f8b4a3aa4490794ca37183"))]) (emms . - [(19) + [(20) ((cl-lib (0 5)) (nadvice @@ -1423,7 +1423,7 @@ (:maintainer "Yoni Rabkin" . "yrk@gnu.org") (:authors ("Jorgen Schäfer" . "forcer@forcix.cx")) - (:commit . "bbe1d2cdc8340e0ea1a70bded0a4d8e0aa7df5c5"))]) + (:commit . "2c328f0a4d46c008d409bbe44994588816adf221"))]) (engrave-faces . [(0 3 1) ((emacs @@ -2915,7 +2915,7 @@ ("Omar Antolín Camarena" . "omar@matem.unam.mx")) (:commit . "3847f311077efa17951a786d2759f2639c5f43c8"))]) (org . - [(9 7 1) + [(9 7 3) ((emacs (26 1))) "Outline-based notes management and organizer" tar @@ -2924,7 +2924,7 @@ (:maintainer "Bastien Guerry" . "bzg@gnu.org") (:authors ("Carsten Dominik" . "carsten.dominik@gmail.com")) - (:commit . "f737e7213d65f6913c9ec1c792539d4fb6619c02"))]) + (:commit . "2f184485bfba4a192ed10ceabb26bbec2154b099"))]) (org-contacts . [(1 1) ((emacs @@ -2967,7 +2967,7 @@ ("Hanno Perrey" . "hanno@hoowl.se")) (:commit . "020b03f299dad438f65d7bcbf93553b273fd7c33"))]) (org-modern . - [(1 2) + [(1 3) ((emacs (27 1)) (compat @@ -2978,7 +2978,7 @@ (:maintainer "Daniel Mendler" . "mail@daniel-mendler.de") (:authors ("Daniel Mendler" . "mail@daniel-mendler.de")) - (:commit . "a2ff4c8e9cac412e8cb9c7faf618ac18146107ea"))]) + (:commit . "0b7af08548e586c0d3b0ca4a683253da407220d1"))]) (org-notify . [(0 1 1) ((emacs @@ -3405,7 +3405,7 @@ ("Alex Schroeder" . "alex@gnu.org")) (:maintainer "Alex Schroeder" . "alex@gnu.org"))]) (rcirc-sqlite . - [(1 0 1) + [(1 0 2) ((emacs (30 0))) "rcirc logging in SQLite" tar @@ -3414,7 +3414,7 @@ (:maintainer "Matto Fransen" . "matto@matto.nl") (:authors ("Matto Fransen" . "matto@matto.nl")) - (:commit . "a2dca3182572ab2af7018a91186dbf9bcb2db2b5"))]) + (:commit . "50279aa1aa4303bd2736de615414120e851db1dd"))]) (realgud . [(1 5 1) ((load-relative diff --git a/emacs/elpa/archives/gnu/archive-contents.signed b/emacs/elpa/archives/gnu/archive-contents.signed @@ -1 +1 @@ -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) <elpasign@elpa.gnu.org> (trust undefined) created at 2024-06-02T09:05:03+0000 using EDDSA -\ No newline at end of file +Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) <elpasign@elpa.gnu.org> (trust undefined) created at 2024-06-09T09:05:03+0000 using EDDSA +\ No newline at end of file diff --git a/emacs/elpa/archives/melpa/archive-contents b/emacs/elpa/archives/melpa/archive-contents @@ -213,7 +213,7 @@ (auth-source-xoauth2 . [(20220804 2219) ((emacs (26 1))) "Integrate auth-source with XOAUTH2" tar ((:commit . "99a03f8ce835412943d311b2746e77fcf5a1b500") (:authors ("Cesar Crusius" . "ccrusius@google.com")) (:maintainers ("Cesar Crusius" . "ccrusius@google.com")) (:maintainer "Cesar Crusius" . "ccrusius@google.com") (:url . "https://github.com/ccrusius/auth-source-xoauth2"))]) (auto-async-byte-compile . [(20160916 454) nil "Automatically byte-compile when saved" tar ((:commit . "8681e74ddb8481789c5dbb3cafabb327db4c4484") (:authors ("rubikitch" . "rubikitch@ruby-lang.org")) (:maintainers ("rubikitch" . "rubikitch@ruby-lang.org")) (:maintainer "rubikitch" . "rubikitch@ruby-lang.org") (:keywords "lisp" "convenience") (:url . "http://www.emacswiki.org/cgi-bin/wiki/download/auto-async-byte-compile.el"))]) (auto-auto-indent . [(20131106 1903) ((es-lib (0 1)) (cl-lib (1 0))) "Indents code as you type" tar ((:commit . "0139378577f936d34b20276af6f022fb457af490") (:authors ("sabof")) (:maintainers ("sabof")) (:maintainer "sabof") (:url . "https://github.com/sabof/auto-auto-indent"))]) - (auto-compile . [(20240415 1533) ((emacs (26 1))) "Automatically compile Emacs Lisp libraries" tar ((:commit . "01844064e3f2bb9f109a8a064998baf89a864fbc") (:authors ("Jonas Bernoulli" . "emacs.auto-compile@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.auto-compile@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.auto-compile@jonas.bernoulli.dev") (:keywords "compile" "convenience" "lisp") (:url . "https://github.com/emacscollective/auto-compile"))]) + (auto-compile . [(20240607 2343) ((emacs (26 1))) "Automatically compile Emacs Lisp libraries" tar ((:commit . "fc5b66cc6ee0f7dcf0b54ce576f46c0e2fa8c4e8") (:authors ("Jonas Bernoulli" . "emacs.auto-compile@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.auto-compile@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.auto-compile@jonas.bernoulli.dev") (:keywords "compile" "convenience" "lisp") (:url . "https://github.com/emacscollective/auto-compile"))]) (auto-complete . [(20240320 1734) ((popup (0 5 0)) (cl-lib (0 5))) "Auto Completion for GNU Emacs" tar ((:commit . "0c2f5a7d28b70bfe30b87378d58d74798a62741d") (:authors ("Tomohiro Matsuyama" . "m2ym.pub@gmail.com")) (:maintainer "Jen-Chieh Shen" . "jcs090218@gmail.com") (:keywords "completion" "convenience") (:url . "https://github.com/auto-complete/auto-complete"))]) (auto-complete-auctex . [(20140223 1758) ((yasnippet (0 6 1)) (auto-complete (1 4))) "auto-completion for auctex" tar ((:commit . "855633f668bcc4b9408396742a7cb84e0c4a2f77") (:authors ("Christopher Monsanto" . "chris@monsan.to")) (:maintainers ("Christopher Monsanto" . "chris@monsan.to")) (:maintainer "Christopher Monsanto" . "chris@monsan.to"))]) (auto-complete-c-headers . [(20150912 323) ((auto-complete (1 4))) "An auto-complete source for C/C++ header files" tar ((:commit . "52fef720c6f274ad8de52bef39a343421006c511") (:authors ("Masafumi Oyamada" . "stillpedant@gmail.com")) (:maintainers ("Masafumi Oyamada" . "stillpedant@gmail.com")) (:maintainer "Masafumi Oyamada" . "stillpedant@gmail.com") (:keywords "c"))]) @@ -350,7 +350,7 @@ (biomejs-format . [(20240401 458) ((emacs (24 1))) "Minor mode to format JS code with Biome on file save" tar ((:commit . "cbfb8aac8bfab6fd893f1ccb4eb9efa29b1b3214") (:authors ("James Long and contributors")) (:maintainers ("Kanon Kakuno" . "yadex205@yadex205.com")) (:maintainer "Kanon Kakuno" . "yadex205@yadex205.com") (:keywords "convenience" "wp" "edit" "js") (:url . "https://github.com/yadex205/emacs-biomejs-format"))]) (birds-of-paradise-plus-theme . [(20130419 2129) nil "A brown/orange light-on-dark theme for Emacs 24 (deftheme)." tar ((:commit . "bb9f9d4ef7f7872a388ec4eee1253069adcadb6f") (:authors ("Jim Myhrberg" . "contact@jimeh.me")) (:maintainers ("Jim Myhrberg" . "contact@jimeh.me")) (:maintainer "Jim Myhrberg" . "contact@jimeh.me") (:keywords "themes") (:url . "https://github.com/jimeh/birds-of-paradise-plus-theme.el"))]) (bison-mode . [(20210527 717) nil "Major mode for editing bison, yacc and lex files." tar ((:commit . "4f2e20394a475931409618c1635e9c9f1cf07d9c") (:authors ("Eric Beuscher" . "beuscher@eecs.tulane.edu")) (:maintainers ("Eric Beuscher" . "beuscher@eecs.tulane.edu")) (:maintainer "Eric Beuscher" . "beuscher@eecs.tulane.edu") (:keywords "bison-mode" "yacc-mode"))]) - (bitbake . [(20220509 1236) ((emacs (24 1)) (dash (2 6 0)) (mmm-mode (0 5 4)) (s (1 10 0))) "Running bitbake from emacs" tar ((:commit . "434b088ab8715731d62978264cb934e34c75c4b3") (:authors ("Damien Merenne")) (:maintainers ("Damien Merenne")) (:maintainer "Damien Merenne") (:keywords "convenience") (:url . "https://github.com/canatella/bitbake-el"))]) + (bitbake . [(20240605 1322) ((emacs (24 1)) (dash (2 6 0)) (mmm-mode (0 5 4)) (s (1 10 0))) "Running bitbake from emacs" tar ((:commit . "8285f46fe19cb99fe5ed42d38de0fe5c51c98fb0") (:authors ("Damien Merenne")) (:maintainers ("Damien Merenne")) (:maintainer "Damien Merenne") (:keywords "convenience") (:url . "https://github.com/canatella/bitbake-el"))]) (bitlbee . [(20151203 0) nil "Help get Bitlbee (http://www.bitlbee.org) up and running." tar ((:commit . "f3342da46b0864ae8db4e82b553d9e617b090534"))]) (bitpack . [(20230417 2032) ((emacs (24 3))) "Bit packing functions" tar ((:commit . "38d000646b81ce52fcb90a0747059a15264e112b") (:authors ("Christopher Wellons" . "wellons@nullprogram.com")) (:maintainers ("Christopher Wellons" . "wellons@nullprogram.com")) (:maintainer "Christopher Wellons" . "wellons@nullprogram.com") (:keywords "c" "comm") (:url . "https://github.com/skeeto/bitpack"))]) (blackboard-bold-mode . [(20160813 206) ((cl-lib (0 5))) "Easily insert Unicode mathematical double-struck characters" tar ((:commit . "5299cb064ba71baa3e331b8560bf8dd38cbbc4ed") (:authors ("Grant Rettke" . "gcr@wisdomandwonder.com")) (:maintainers (nil . "<gcr@wisdomandwonder.com>")) (:maintainer nil . "<gcr@wisdomandwonder.com>") (:keywords "unicode" "double struck" "blackboard bold" "math" "mathematical") (:url . "https://github.com/grettke/blackboard-bold-mode"))]) @@ -386,7 +386,7 @@ (bookmark-view . [(20240102 334) ((emacs (27 1))) "Bookmark views" tar ((:commit . "2d16b2f88a106e57c58ad2af1f7166a847996512") (:authors ("Daniel Mendler")) (:maintainers ("Daniel Mendler")) (:maintainer "Daniel Mendler") (:url . "https://github.com/minad/bookmark-view"))]) (bool-flip . [(20161215 1539) ((emacs (24 3))) "flip the boolean under the point" tar ((:commit . "0f7cc9b387429239fb929896511727d4e49a795b") (:authors ("Michael Brandt" . "michaelbrandt5@gmail.com")) (:maintainers ("Michael Brandt" . "michaelbrandt5@gmail.com")) (:maintainer "Michael Brandt" . "michaelbrandt5@gmail.com") (:keywords "boolean" "convenience" "usability") (:url . "http://github.com/michaeljb/bool-flip/"))]) (boon . [(20240314 920) ((emacs (26 1)) (dash (2 12 0)) (expand-region (0 10 0)) (multiple-cursors (1 3 0))) "Ergonomic Command Mode for Emacs." tar ((:commit . "9e006726be9ac508e0bb0393393abce06f7493f4"))]) - (borg . [(20240415 1553) ((emacs (27 1)) (epkg (3 3 3)) (magit (3 3 0))) "Assimilate Emacs packages as Git submodules" tar ((:commit . "dfc5d58b439042a6e323cb342ab7531692d118a0") (:authors ("Jonas Bernoulli" . "emacs.borg@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.borg@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.borg@jonas.bernoulli.dev") (:keywords "tools") (:url . "https://github.com/emacscollective/borg"))]) + (borg . [(20240609 1435) ((emacs (27 1)) (epkg (3 3 3)) (magit (3 3 0))) "Assimilate Emacs packages as Git submodules" tar ((:commit . "940dc6af73cc40b5254fdb15ff1a6058e1b2b8a5") (:authors ("Jonas Bernoulli" . "emacs.borg@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.borg@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.borg@jonas.bernoulli.dev") (:keywords "tools") (:url . "https://github.com/emacscollective/borg"))]) (borland-blue-theme . [(20160117 1321) ((emacs (24 1))) "Blue/yellow theme based on old DOS Borland/Turbo C IDE" tar ((:commit . "db74eefebbc89d3c62575f8f50b319e87b4a3470") (:authors ("Alexey Veretennikov <alexey dot veretennikov at gmail dot com>")) (:maintainers ("Alexey Veretennikov <alexey dot veretennikov at gmail dot com>")) (:maintainer "Alexey Veretennikov <alexey dot veretennikov at gmail dot com>") (:keywords "themes") (:url . "http://github.com/fourier/borland-blue-theme"))]) (boron-theme . [(20170808 1308) ((emacs (24 0))) "an Emacs 24 theme based on Boron (tmTheme)" tar ((:commit . "87ae1a765e07429fec25d2f29b004f84b52d2e0a") (:authors ("Jason Milkins")) (:maintainers ("Jason Milkins")) (:maintainer "Jason Milkins") (:url . "https://github.com/emacsfodder/tmtheme-to-deftheme"))]) (boxes . [(20240217 1143) ((emacs (24 3))) "ASCII boxes unlimited!" tar ((:commit . "75dfd61801b3ec23ec30c88640ea31bbca5b36b9") (:authors ("Jason L. Shiffer" . "jshiffer@zerotao.com")) (:maintainers ("Jason L. Shiffer" . "jshiffer@zerotao.com")) (:maintainer "Jason L. Shiffer" . "jshiffer@zerotao.com") (:keywords "extensions") (:url . "https://boxes.thomasjensen.com"))]) @@ -398,7 +398,7 @@ (bracketed-paste . [(20160407 2348) ((emacs (24 3))) "bracketed paste mode support within emacs -nw" tar ((:commit . "843ce3bbb63d560face889e13a57a2f7543957d5") (:authors ("Takeshi Banse" . "takebi@laafc.net")) (:maintainers ("Takeshi Banse" . "takebi@laafc.net")) (:maintainer "Takeshi Banse" . "takebi@laafc.net") (:keywords "terminals"))]) (brainfuck-mode . [(20150113 842) ((langdoc (20130601 1450))) "Brainfuck mode for Emacs" tar ((:commit . "36e69552bb3b97a4f888d362c59845651bd0d492") (:authors ("Tomoya Tanjo" . "ttanjo@gmail.com")) (:maintainers ("Tomoya Tanjo" . "ttanjo@gmail.com")) (:maintainer "Tomoya Tanjo" . "ttanjo@gmail.com") (:keywords "brainfuck" "langdoc") (:url . "https://github.com/tom-tan/brainfuck-mode/"))]) (brazilian-holidays . [(20220828 2348) ((emacs (26))) "Brazilian holidays" tar ((:commit . "03206ea673df49c91a8f924db799620713d86240") (:authors ("Jaguaraquem A. Reinaldo" . "jaguar.adler@gmail.com")) (:maintainers ("Jaguaraquem A. Reinaldo" . "jaguar.adler@gmail.com")) (:maintainer "Jaguaraquem A. Reinaldo" . "jaguar.adler@gmail.com") (:keywords "calendar" "holidays" "brazilian") (:url . "https://github.com/jadler/brazilian-holidays"))]) - (brec-mode . [(20240507 504) ((emacs (24 3))) "A major mode for editing Breccian text" tar ((:commit . "dafb49308796f75db802e2d23596dfd3fb7e37b0") (:authors ("Michael Allan" . "mike@reluk.ca")) (:maintainers ("Michael Allan" . "mike@reluk.ca")) (:maintainer "Michael Allan" . "mike@reluk.ca") (:keywords "outlines" "wp") (:url . "http://reluk.ca/project/Breccia/Emacs/"))]) + (brec-mode . [(20240607 436) ((emacs (24 3))) "A major mode for editing Breccian text" tar ((:commit . "78c3dbb79b0cc1074ac0c8de734c579c1bc15a31") (:authors ("Michael Allan" . "mike@reluk.ca")) (:maintainers ("Michael Allan" . "mike@reluk.ca")) (:maintainer "Michael Allan" . "mike@reluk.ca") (:keywords "outlines" "wp") (:url . "http://reluk.ca/project/Breccia/Emacs/"))]) (brf . [(20230803 2022) ((fringe-helper (0 1 1)) (emacs (24 3))) "Brf-mode provides features from the legendary editor Brief" tar ((:commit . "8875f5fcd173e220bbfa6bf9f8f09d721a29cd50") (:authors ("Mike Woolley" . "mike@bulsara.com")) (:maintainers ("Mike Woolley" . "mike@bulsara.com")) (:maintainer "Mike Woolley" . "mike@bulsara.com") (:keywords "brief" "crisp" "emulations") (:url . "https://bitbucket.org/MikeWoolley/brf-mode"))]) (brightscript-mode . [(20220906 827) ((emacs (26 3))) "Major mode for editing Brightscript files" tar ((:commit . "025d6f5a70752c62a28d4f86c053a283b3898a49") (:authors ("Daniel Mircea" . "daniel@viseztrance.com")) (:maintainers (nil . "daniel@viseztrance.com")) (:maintainer nil . "daniel@viseztrance.com") (:keywords "languages") (:url . "https://github.com/viseztrance/brightscript-mode"))]) (bril-mode . [(20240315 1157) ((emacs (27 1))) "Major mode for Bril text format" tar ((:commit . "da61316385e31973c462a1e8a3213327b34df3ff") (:authors ("Noah Peart" . "noah.v.peart@gmail.com")) (:maintainers ("Noah Peart" . "noah.v.peart@gmail.com")) (:maintainer "Noah Peart" . "noah.v.peart@gmail.com") (:keywords "languages" "bril") (:url . "https://github.com/nverno/bril-mode"))]) @@ -406,7 +406,7 @@ (browse-at-remote . [(20230223 554) ((f (0 20 0)) (s (1 9 0)) (cl-lib (0 5))) "Open github/gitlab/bitbucket/stash/gist/phab/sourcehut page from Emacs" tar ((:commit . "1c2a565bb7275bf78f23d471e32dd8c696523b8c") (:authors ("Rustem Muslimov" . "r.muslimov@gmail.com")) (:maintainers ("Rustem Muslimov" . "r.muslimov@gmail.com")) (:maintainer "Rustem Muslimov" . "r.muslimov@gmail.com") (:keywords "github" "gitlab" "bitbucket" "gist" "stash" "phabricator" "sourcehut" "pagure") (:url . "https://github.com/rmuslimov/browse-at-remote"))]) (browse-kill-ring . [(20231104 1450) nil "interactively insert items from kill-ring" tar ((:commit . "03cc18c08a549568edb6bc710c307c19bc507ef3") (:authors ("Colin Walters" . "walters@verbum.org")) (:maintainers ("browse-kill-ring" . "browse-kill-ring@tonotdo.com")) (:maintainer "browse-kill-ring" . "browse-kill-ring@tonotdo.com") (:keywords "convenience") (:url . "https://github.com/browse-kill-ring/browse-kill-ring"))]) (browse-url-dwim . [(20140731 1922) ((string-utils (0 3 2))) "Context-sensitive external browse URL or Internet search" tar ((:commit . "11f1c53126619c7ef1bb5f5d6914ce0b3cce0e30") (:authors ("Roland Walker" . "walker@pobox.com")) (:maintainers ("Roland Walker" . "walker@pobox.com")) (:maintainer "Roland Walker" . "walker@pobox.com") (:keywords "hypermedia") (:url . "http://github.com/rolandwalker/browse-url-dwim"))]) - (browser-hist . [(20240402 2345) ((emacs (27))) "Search through the Browser history" tar ((:commit . "0b7e93274173e82c05e5d9c9d3055e4b9017612a") (:authors ("Ag Ibragimov" . "agzam.ibragimov@gmail.com")) (:maintainers ("Ag Ibragimov" . "agzam.ibragimov@gmail.com")) (:maintainer "Ag Ibragimov" . "agzam.ibragimov@gmail.com") (:keywords "convenience" "hypermedia" "matching" "tools") (:url . "https://github.com/agzam/browser-hist.el"))]) + (browser-hist . [(20240607 406) ((emacs (27))) "Search through the Browser history" tar ((:commit . "0372c6d984ca194d9454b14eba6eadec480ec3ff") (:authors ("Ag Ibragimov" . "agzam.ibragimov@gmail.com")) (:maintainers ("Ag Ibragimov" . "agzam.ibragimov@gmail.com")) (:maintainer "Ag Ibragimov" . "agzam.ibragimov@gmail.com") (:keywords "convenience" "hypermedia" "matching" "tools") (:url . "https://github.com/agzam/browser-hist.el"))]) (brutalist-theme . [(20231120 721) nil "Brutalist theme" tar ((:commit . "c387f3f0aaae147270c61dcd3140fb4eb20965ad") (:authors ("Gergely Nagy")) (:maintainers ("Gergely Nagy")) (:maintainer "Gergely Nagy") (:url . "https://git.madhouse-project.org/algernon/brutalist-theme.el"))]) (bshell . [(20230805 1646) ((emacs (26)) (buffer-manage (1 1))) "Manage and track multiple inferior shells" tar ((:commit . "57f3409168ec9649508e3ee30d0d2de8f81b960e") (:authors ("Paul Landes")) (:maintainers ("Paul Landes")) (:maintainer "Paul Landes") (:keywords "unix" "interactive" "shell" "management") (:url . "https://github.com/plandes/bshell"))]) (btc-ticker . [(20220409 1647) ((json (1 2)) (request (0 2 0))) "Shows latest bitcoin price" tar ((:commit . "2ed18ac6338d5fe98c578f0875840af07f0bc42a") (:authors ("Jorge Niedbalski R." . "jnr@metaklass.org")) (:maintainers ("Jorge Niedbalski R." . "jnr@metaklass.org")) (:maintainer "Jorge Niedbalski R." . "jnr@metaklass.org") (:keywords "news"))]) @@ -488,16 +488,17 @@ (cask-mode . [(20160410 1449) ((emacs (24 3))) "major mode for editing Cask files" tar ((:commit . "c97755267b7215f02df7b0c16b4210c04aee6566") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainers ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk"))]) (cask-package-toolset . [(20170921 2256) ((emacs (24)) (cl-lib (0 3)) (s (1 6 1)) (dash (1 8 0)) (f (0 10 0)) (commander (0 2 0)) (ansi (0 1 0)) (shut-up (0 1 0))) "Toolsettize your package" tar ((:commit . "2c74cd827e88c7f8360581a841e45f0b794510e7") (:authors ("Adrien Becchis" . "adriean.khisbe@live.fr")) (:maintainers ("Adrien Becchis" . "adriean.khisbe@live.fr")) (:maintainer "Adrien Becchis" . "adriean.khisbe@live.fr") (:keywords "convenience" "tools") (:url . "http://github.com/AdrieanKhisbe/cask-package-toolset.el"))]) (caskxy . [(20140513 1539) ((log4e (0 2 0)) (yaxception (0 1))) "Control Cask in Emacs" tar ((:commit . "279f3ab79bd77fe69cb3148a79896b9bf118a9b3") (:authors ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainers ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainer "Hiroaki Otsu" . "ootsuhiroaki@gmail.com") (:keywords "convenience") (:url . "https://github.com/aki2o/caskxy"))]) - (casual . [(20240416 2237) ((emacs (29 1))) "Transient UI for Calc" tar ((:commit . "a22cf128c3baa3e11f6aaff7dc44ef91cf0fe9ce") (:authors ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainers ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainer "Charles Choi" . "kickingvegas@gmail.com") (:keywords "tools") (:url . "https://github.com/kickingvegas/casual"))]) - (casual-avy . [(20240529 1711) ((emacs (29 1)) (avy (0 5 0))) "Transient UI for Avy" tar ((:commit . "ab5d952a40ee868f43c48a6207644b4fd54768c0") (:authors ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainers ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainer "Charles Choi" . "kickingvegas@gmail.com") (:keywords "tools") (:url . "https://github.com/kickingvegas/casual-avy"))]) - (casual-dired . [(20240523 1755) ((emacs (29 1))) "Casual Dired" tar ((:commit . "f1dc88480a3847f1defa2dce7efb6d8234eb5b8b") (:authors ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainers ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainer "Charles Choi" . "kickingvegas@gmail.com") (:keywords "tools") (:url . "https://github.com/kickingvegas/casual-dired"))]) + (casual . [(20240607 1653) ((emacs (29 1))) "Transient UI for Calc" tar ((:commit . "7f46e48364335fa8957a1bb4a71ed4589a48f298") (:authors ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainers ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainer "Charles Choi" . "kickingvegas@gmail.com") (:keywords "tools") (:url . "https://github.com/kickingvegas/casual"))]) + (casual-avy . [(20240607 11) ((emacs (29 1)) (avy (0 5 0))) "Transient UI for Avy" tar ((:commit . "2e78a44568632532ef133005670c42becc7d6c58") (:authors ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainers ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainer "Charles Choi" . "kickingvegas@gmail.com") (:keywords "tools") (:url . "https://github.com/kickingvegas/casual-avy"))]) + (casual-dired . [(20240607 14) ((emacs (29 1))) "Transient UI for Dired" tar ((:commit . "66ba7b7955457d093cc61f2931cb4bfbd8273069") (:authors ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainers ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainer "Charles Choi" . "kickingvegas@gmail.com") (:keywords "tools") (:url . "https://github.com/kickingvegas/casual-dired"))]) + (casual-info . [(20240607 15) ((emacs (29 1))) "A Transient UI for Info" tar ((:commit . "70073a736e1d077a1e117daab2719b77296586f1") (:authors ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainers ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainer "Charles Choi" . "kickingvegas@gmail.com") (:keywords "tools") (:url . "https://github.com/kickingvegas/casual-info"))]) (catmacs . [(20170826 1157) ((emacs (24))) "Simple CAT interface for Yaesu Transceivers." tar ((:commit . "6ea9ee195661fe95355413856476c45dcc8e24e8") (:authors ("Frank Singleton" . "b17flyboy@gmail.com")) (:maintainers ("Frank Singleton" . "b17flyboy@gmail.com")) (:maintainer "Frank Singleton" . "b17flyboy@gmail.com") (:keywords "comm" "hardware") (:url . "https://bitbucket.org/pymaximus/catmacs"))]) - (catppuccin-theme . [(20240326 900) ((emacs (25 1))) "Catppuccin for Emacs - 🍄 Soothing pastel theme for Emacs" tar ((:commit . "3d93abaa33e95f19b4a8b0e1e9bef1e3e68dd994") (:authors ("nyxkrage")) (:maintainers ("Carsten Kragelund" . "carsten@kragelund.me")) (:maintainer "Carsten Kragelund" . "carsten@kragelund.me") (:url . "https://github.com/catppuccin/emacs"))]) + (catppuccin-theme . [(20240607 1703) ((emacs (25 1))) "Catppuccin for Emacs - 🍄 Soothing pastel theme for Emacs" tar ((:commit . "2f15c7c7cca7834b072bb26421a2755a67786899") (:authors ("nyxkrage")) (:maintainers ("Carsten Kragelund" . "carsten@kragelund.me")) (:maintainer "Carsten Kragelund" . "carsten@kragelund.me") (:url . "https://github.com/catppuccin/emacs"))]) (cats . [(20230407 1316) ((emacs (26 1))) "Monads for Elisp" tar ((:commit . "7fc70db0eeb2c33ffba5c13c4cdc0f31c7b95537") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainers ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:url . "https://github.com/Fuco1/emacs-cats"))]) (cbm . [(20171116 1240) ((cl-lib (0 5))) "Switch to similar buffers." tar ((:commit . "5b41c936ba9f6d170309a85ffebc9939c1050b31") (:authors ("Lukas Fürmetz" . "fuermetz@mailbox.org")) (:maintainers ("Lukas Fürmetz" . "fuermetz@mailbox.org")) (:maintainer "Lukas Fürmetz" . "fuermetz@mailbox.org") (:keywords "buffers") (:url . "http://github.com/akermu/cbm.el"))]) (cbor . [(20230810 1653) ((emacs (25 1))) "CBOR utilities" tar ((:commit . "ba624ad3f8b726bee1d8dcb0a2a9e2b658bb4c9b") (:authors ("Oscar Najera <https://oscarnajera.com>")) (:maintainers ("Oscar Najera" . "hi@oscarnajera.com")) (:maintainer "Oscar Najera" . "hi@oscarnajera.com") (:url . "https://github.com/Titan-C/cardano.el"))]) (cc-cedict . [(20231209 1109) ((emacs (26 1))) "Interface to CC-CEDICT (a Chinese-English dictionary)" tar ((:commit . "0c124beae160d5ff9be927bfb5e1a5fd8d50817a") (:authors ("Xu Chunyang")) (:maintainers ("Xu Chunyang")) (:maintainer "Xu Chunyang") (:url . "https://github.com/xuchunyang/cc-cedict.el"))]) - (cc-isearch-menu . [(20240401 48) ((emacs (29 1))) "A Transient menu for isearch" tar ((:commit . "6eecc77a89ea63cab69fc8eb6dd1c32ad582b05b") (:authors ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainers ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainer "Charles Choi" . "kickingvegas@gmail.com") (:keywords "wp") (:url . "https://github.com/kickingvegas/cc-isearch-menu"))]) + (cc-isearch-menu . [(20240607 7) ((emacs (29 1))) "A Transient UI for I-Search" tar ((:commit . "deaf5c8b63406607a9b990bef5af86c460268a35") (:authors ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainers ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainer "Charles Choi" . "kickingvegas@gmail.com") (:keywords "wp") (:url . "https://github.com/kickingvegas/cc-isearch-menu"))]) (ccc . [(20210501 820) nil "buffer local cursor color control library" tar ((:commit . "36fb9f7e527f975d333887fd0cca4d611ae1ab23") (:authors ("Masatake YAMATO" . "masata-y@is.aist-nara.ac.jp")) (:maintainers ("SKK Development Team")) (:maintainer "SKK Development Team") (:keywords "cursor") (:url . "https://github.com/skk-dev/ddskk"))]) (ccls . [(20240331 2132) ((emacs (27 1)) (lsp-mode (6 3 1)) (dash (2 14 1))) "ccls client for lsp-mode" tar ((:commit . "9c91aad768d5c401295c79f341c5296b69b29490") (:authors ("Tobias Pisani, Fangrui Song")) (:maintainers ("Tobias Pisani, Fangrui Song")) (:maintainer "Tobias Pisani, Fangrui Song") (:keywords "languages" "lsp" "c++") (:url . "https://github.com/emacs-lsp/emacs-ccls"))]) (cd-compile . [(20141108 1957) nil "run compile in a specific directory" tar ((:commit . "10284ccae86afda4a37b09ba90acd1e2efedec9f") (:authors ("Jamie Nicol" . "jamie@thenicols.net")) (:maintainers ("Jamie Nicol" . "jamie@thenicols.net")) (:maintainer "Jamie Nicol" . "jamie@thenicols.net"))]) @@ -560,7 +561,7 @@ (chronos . [(20240525 1339) ((emacs (27 1))) "Multiple simultaneous countdown / countup timers" tar ((:commit . "5ea0bf7c3881ea905e280446342539b242401979") (:authors ("David Knight" . "dxknight@opmbx.org")) (:maintainers ("David Knight" . "dxknight@opmbx.org")) (:maintainer "David Knight" . "dxknight@opmbx.org") (:keywords "calendar") (:url . "http://github.com/DarkBuffalo/chronos"))]) (chruby . [(20180114 1652) ((cl-lib (0 5))) "Emacs integration for chruby" tar ((:commit . "42bc6d521f832eca8e2ba210f30d03ad5529788f") (:authors ("Arne Brasseur" . "arne@arnebrasseur.net")) (:maintainers ("Arne Brasseur" . "arne@arnebrasseur.net")) (:maintainer "Arne Brasseur" . "arne@arnebrasseur.net") (:keywords "languages") (:url . "https://github.com/plexus/chruby.el"))]) (chyla-theme . [(20231220 1545) nil "chyla.org - green color theme." tar ((:commit . "4d4b9dca3547e919ed5311cc7d04821f77860fbd") (:authors ("Adam Chyła" . "adam@chyla.org")) (:maintainers ("Adam Chyła" . "adam@chyla.org")) (:maintainer "Adam Chyła" . "adam@chyla.org") (:url . "https://github.com/chyla/ChylaThemeForEmacs"))]) - (cider . [(20240602 1213) ((emacs (26)) (clojure-mode (5 19)) (parseedn (1 2 1)) (queue (0 2)) (spinner (1 7)) (seq (2 22)) (sesman (0 3 2)) (transient (0 4 1))) "Clojure Interactive Development Environment that Rocks" tar ((:commit . "5f79b02fda70179349ba34a4fed1436708c669c3") (:authors ("Tim King" . "kingtim@gmail.com") ("Phil Hagelberg" . "technomancy@gmail.com") ("Bozhidar Batsov" . "bozhidar@batsov.dev") ("Artur Malabarba" . "bruce.connor.am@gmail.com") ("Hugo Duncan" . "hugo@hugoduncan.org") ("Steve Purcell" . "steve@sanityinc.com")) (:maintainers ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "languages" "clojure" "cider") (:url . "https://www.github.com/clojure-emacs/cider"))]) + (cider . [(20240608 2244) ((emacs (26)) (clojure-mode (5 19)) (parseedn (1 2 1)) (queue (0 2)) (spinner (1 7)) (seq (2 22)) (sesman (0 3 2)) (transient (0 4 1))) "Clojure Interactive Development Environment that Rocks" tar ((:commit . "05e7570e33d1aa795b1a0f85a6aa2162b42b93cd") (:authors ("Tim King" . "kingtim@gmail.com") ("Phil Hagelberg" . "technomancy@gmail.com") ("Bozhidar Batsov" . "bozhidar@batsov.dev") ("Artur Malabarba" . "bruce.connor.am@gmail.com") ("Hugo Duncan" . "hugo@hugoduncan.org") ("Steve Purcell" . "steve@sanityinc.com")) (:maintainers ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "languages" "clojure" "cider") (:url . "https://www.github.com/clojure-emacs/cider"))]) (cider-decompile . [(20151122 537) ((cider (0 3 0)) (javap-mode (9))) "decompilation extension for cider" tar ((:commit . "5d87035f3c3c14025e8f01c0c53d0ce2c8f56651") (:authors ("Dmitry Bushenko")) (:maintainers ("Dmitry Bushenko")) (:maintainer "Dmitry Bushenko") (:keywords "languages" "clojure" "cider") (:url . "http://www.github.com/clojure-emacs/cider-decompile"))]) (cider-eval-sexp-fu . [(20190311 2152) ((emacs (24)) (eval-sexp-fu (0 5 0))) "Briefly highlights an evaluated sexp." tar ((:commit . "7fd229f1441356866aedba611fd0cf4e89b50921") (:authors ("Sylvain Benner" . "sylvain.benner@gmail.com")) (:maintainers ("Sylvain Benner" . "sylvain.benner@gmail.com")) (:maintainer "Sylvain Benner" . "sylvain.benner@gmail.com") (:keywords "languages" "clojure" "cider"))]) (cider-hydra . [(20190816 1121) ((cider (0 22 0)) (hydra (0 13 0))) "Hydras for CIDER." tar ((:commit . "c3b8a15d72dddfbc390ab6a454bd7e4c765a2c95") (:authors ("Tianxiang Xiong" . "tianxiang.xiong@gmail.com")) (:maintainers ("Tianxiang Xiong" . "tianxiang.xiong@gmail.com")) (:maintainer "Tianxiang Xiong" . "tianxiang.xiong@gmail.com") (:keywords "convenience" "tools") (:url . "https://github.com/clojure-emacs/cider-hydra"))]) @@ -568,7 +569,7 @@ (cil-mode . [(20160622 1431) nil "Common Intermediate Language mode" tar ((:commit . "a78a88ca9a66a82f069329a96e34b67478ae2d9b") (:authors ("Friedrich von Never" . "friedrich@fornever.me")) (:maintainers ("Friedrich von Never" . "friedrich@fornever.me")) (:maintainer "Friedrich von Never" . "friedrich@fornever.me") (:keywords "languages") (:url . "https://github.com/ForNeVeR/cil-mode"))]) (cilk-mode . [(20220807 1629) ((emacs (25 1)) (flycheck (32 -4))) "Minor mode for Cilk code editing" tar ((:commit . "d5ba732a5a313a97a96085943cd7840b8e2d9c7c") (:authors ("Alexandros-Stavros Iliopoulos <https://github.com/ailiop>")) (:maintainers ("Alexandros-Stavros Iliopoulos" . "1577182+ailiop@users.noreply.github.com")) (:maintainer "Alexandros-Stavros Iliopoulos" . "1577182+ailiop@users.noreply.github.com") (:keywords "c" "convenience" "faces" "languages") (:url . "https://github.com/ailiop/cilk-mode"))]) (cinspect . [(20150716 233) ((emacs (24)) (cl-lib (0 5)) (deferred (0 3 1)) (python-environment (0 0 2))) "Use cinspect to look at the CPython source of builtins and other C objects!" tar ((:commit . "4e199a90f89b335cccda1518aa0963e0a1d4fbab") (:authors ("Ben Yelsey" . "ben.yelsey@gmail.com")) (:maintainers ("Ben Yelsey" . "ben.yelsey@gmail.com")) (:maintainer "Ben Yelsey" . "ben.yelsey@gmail.com") (:keywords "python") (:url . "https://github.com/inlinestyle/cinspect-mode"))]) - (circadian . [(20221223 1419) ((emacs (24 4))) "Theme-switching based on daytime" tar ((:commit . "f20cdbf164be10ef0c55d26eba4d270c7c826f42") (:authors ("Guido Schmidt")) (:maintainers ("Guido Schmidt" . "git@guidoschmidt.cc")) (:maintainer "Guido Schmidt" . "git@guidoschmidt.cc") (:keywords "themes") (:url . "https://github.com/GuidoSchmidt/circadian"))]) + (circadian . [(20240603 935) ((emacs (27 2))) "Theme-switching based on daytime" tar ((:commit . "76464419f69e9758bc5a76b2420c9648ddf93dba") (:authors ("Guido Schmidt")) (:maintainers ("Guido Schmidt" . "git@guidoschmidt.cc")) (:maintainer "Guido Schmidt" . "git@guidoschmidt.cc") (:keywords "themes") (:url . "https://github.com/GuidoSchmidt/circadian"))]) (circe . [(20240407 1101) ((emacs (25 1)) (cl-lib (0 5))) "Client for IRC in Emacs" tar ((:commit . "9d703f481a2c65f2b17edcc2b05412f9865d24af") (:authors ("Jorgen Schaefer" . "forcer@forcix.cx")) (:maintainer "Jorgen Schaefer" . "forcer@forcix.cx") (:keywords "irc" "chat" "comm") (:url . "https://github.com/emacs-circe/circe"))]) (circe-notifications . [(20180102 2318) ((emacs (24 4)) (circe (2 3)) (alert (1 2))) "Add desktop notifications to Circe." tar ((:commit . "291149ac12877bbd062da993479d3533a26862b0") (:authors ("Ruben Maher" . "r@rkm.id.au")) (:maintainers ("Ruben Maher" . "r@rkm.id.au")) (:maintainer "Ruben Maher" . "r@rkm.id.au") (:url . "https://github.com/eqyiel/circe-notifications"))]) (circleci-api . [(20210227 1607) ((emacs (27)) (request (0 3 2))) "Bindings for the CircleCI API" tar ((:commit . "1432b0ad0f32b03fec564c0815951d5e096c2f6a") (:authors ("Robin Schroer")) (:maintainers ("Robin Schroer")) (:maintainer "Robin Schroer") (:url . "https://github.com/sulami/circleci-api"))]) @@ -619,9 +620,9 @@ (clojure-mode-extra-font-locking . [(20240526 1824) ((clojure-mode (3 0))) "Extra font-locking for Clojure mode" tar ((:commit . "4afdd3539036bbd6b1c01b2e00559676c4d40085") (:authors ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainers ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "languages" "lisp") (:url . "https://github.com/clojure-emacs/clojure-mode"))]) (clojure-quick-repls . [(20150814 736) ((cider (0 8 1)) (dash (2 9 0))) "Quickly create Clojure and ClojureScript repls for a project." tar ((:commit . "8fe4e44939e8a01a4cdf60c0001d9a6abf8a73c3") (:keywords "languages" "clojure" "cider" "clojurescript") (:url . "https://github.com/symfrog/clojure-quick-repls"))]) (clojure-snippets . [(20220914 950) ((yasnippet (0 10 0))) "Yasnippets for clojure" tar ((:commit . "66d23f0ffedf2cc2be0387c3504b5f89d7300cfa") (:authors ("Max Penet" . "m@qbits.cc")) (:maintainer "Max Penet" . "m@qbits.cc") (:keywords "snippets"))]) - (clojure-ts-mode . [(20240314 552) ((emacs (29 1))) "Major mode for Clojure code" tar ((:commit . "8afa5656955814193b3b27020faf4edf00abda88") (:maintainers ("Danny Freeman" . "danny@dfreeman.email")) (:maintainer "Danny Freeman" . "danny@dfreeman.email") (:keywords "languages" "clojure" "clojurescript" "lisp") (:url . "http://github.com/clojure-emacs/clojure-ts-mode"))]) + (clojure-ts-mode . [(20240604 746) ((emacs (29 1))) "Major mode for Clojure code" tar ((:commit . "649bf1120f10250d464d4e9ad1905b481d2e504c") (:maintainers ("Danny Freeman" . "danny@dfreeman.email")) (:maintainer "Danny Freeman" . "danny@dfreeman.email") (:keywords "languages" "clojure" "clojurescript" "lisp") (:url . "http://github.com/clojure-emacs/clojure-ts-mode"))]) (clomacs . [(20220415 1035) ((emacs (24 3)) (cider (0 22 1)) (s (1 12 0)) (simple-httpd (1 4 6)) (dash (2 19 1))) "Simplifies Emacs Lisp interaction with Clojure." tar ((:commit . "9cd7c9fd86bc7bc627a31275d1ef131378b90a49") (:authors ("Kostafey" . "kostafey@gmail.com")) (:maintainers ("Kostafey" . "kostafey@gmail.com")) (:maintainer "Kostafey" . "kostafey@gmail.com") (:keywords "clojure" "interaction") (:url . "https://github.com/clojure-emacs/clomacs"))]) - (closql . [(20240531 1836) ((emacs (26 1)) (compat (29 1 4 5)) (emacsql (20240124))) "Store EIEIO objects using EmacSQL" tar ((:commit . "c591e6b310e1e583ca466a8f2c42d3c5d1ada435") (:authors ("Jonas Bernoulli" . "emacs.closql@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.closql@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.closql@jonas.bernoulli.dev") (:keywords "extensions") (:url . "https://github.com/emacscollective/closql"))]) + (closql . [(20240601 1727) ((emacs (26 1)) (compat (29 1 4 5)) (emacsql (20240124))) "Store EIEIO objects using EmacSQL" tar ((:commit . "27b6d2be1a06cdcb5d5fbd77a702b9fbc5082c03") (:authors ("Jonas Bernoulli" . "emacs.closql@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.closql@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.closql@jonas.bernoulli.dev") (:keywords "extensions") (:url . "https://github.com/emacscollective/closql"))]) (closure-lint-mode . [(20101118 2124) nil "minor mode for the Closure Linter" tar ((:commit . "bc3d2fd5c35580bf1b8af43b12484c95a343b4b5") (:authors ("Roman Scherer" . "roman@burningswell.com")) (:maintainers ("Roman Scherer" . "roman@burningswell.com")) (:maintainer "Roman Scherer" . "roman@burningswell.com") (:keywords "tools" "closure" "javascript" "lint" "flymake") (:url . "https://github.com/r0man/closure-lint-mode"))]) (cloud-theme . [(20220205 1336) ((emacs (24))) "A light colored theme" tar ((:commit . "16ef7fbf0a423b29e3c3a0a2d9525afaf265aaed") (:authors ("Valerii Lysenko" . "vallyscode@gmail.com")) (:maintainers ("Valerii Lysenko" . "vallyscode@gmail.com")) (:maintainer "Valerii Lysenko" . "vallyscode@gmail.com") (:keywords "color" "theme") (:url . "https://github.com/vallyscode/cloud-theme"))]) (cloud-to-butt-erc . [(20130627 2308) nil "Replace 'the cloud' with 'my butt'" tar ((:commit . "6710c03d1bc91736435cbfe845924940cae34e5c") (:authors ("David Leatherman" . "leathekd@gmail.com")) (:maintainers ("David Leatherman" . "leathekd@gmail.com")) (:maintainer "David Leatherman" . "leathekd@gmail.com") (:url . "http://www.github.com/leathekd/cloud-to-butt-erc"))]) @@ -755,7 +756,7 @@ (compile-multi . [(20240507 1829) ((emacs (28 1))) "A multi target interface to compile" tar ((:commit . "1fbd38806c70ebe8ebfec49cc7606284396a31e6") (:authors ("mohsin kaleem" . "mohkale@kisara.moe")) (:maintainers ("mohsin kaleem" . "mohkale@kisara.moe")) (:maintainer "mohsin kaleem" . "mohkale@kisara.moe") (:keywords "tools" "compile" "build") (:url . "https://github.com/mohkale/compile-multi"))]) (compile-multi-all-the-icons . [(20240506 1319) ((emacs (28 0)) (all-the-icons-completion (0 0 1))) "Affixate `compile-multi' with icons" tar ((:commit . "4f9cc1d3c448ecc496540a65578191733c09b0fd") (:authors ("mohsin kaleem" . "mohkale@kisara.moe")) (:maintainers ("mohsin kaleem" . "mohkale@kisara.moe")) (:maintainer "mohsin kaleem" . "mohkale@kisara.moe") (:keywords "tools" "compile" "build") (:url . "https://github.com/mohkale/compile-multi"))]) (compile-multi-embark . [(20230904 1806) ((emacs (28 1)) (compile-multi (0 4)) (embark (0 22 1))) "Integration for `compile-multi' and `embark'" tar ((:commit . "30edb0e86287101269debf20f43cead92310029a") (:authors ("Mohsin Kaleem" . "mohkale@kisara.moe")) (:maintainers ("Mohsin Kaleem" . "mohkale@kisara.moe")) (:maintainer "Mohsin Kaleem" . "mohkale@kisara.moe") (:keywords "project" "convenience") (:url . "https://github.com/mohkale/compile-multi"))]) - (compiler-explorer . [(20240530 1705) ((emacs (26 1)) (plz (0 8)) (eldoc (1 15 0)) (map (3 3 1)) (seq (2 23))) "Compiler explorer client (godbolt.org)" tar ((:commit . "689559954dcfa9abfae37962d5127a9922cfb52c") (:authors ("Michał Krzywkowski" . "k.michal@zoho.com")) (:maintainers ("Michał Krzywkowski" . "k.michal@zoho.com")) (:maintainer "Michał Krzywkowski" . "k.michal@zoho.com") (:keywords "c" "tools") (:url . "https://github.com/mkcms/compiler-explorer.el"))]) + (compiler-explorer . [(20240609 1250) ((emacs (26 1)) (plz (0 8)) (eldoc (1 15 0)) (map (3 3 1)) (seq (2 23))) "Compiler explorer client (godbolt.org)" tar ((:commit . "f7b440125264efc043b9d61186e4ac662cb8b67c") (:authors ("Michał Krzywkowski" . "k.michal@zoho.com")) (:maintainers ("Michał Krzywkowski" . "k.michal@zoho.com")) (:maintainer "Michał Krzywkowski" . "k.michal@zoho.com") (:keywords "c" "tools") (:url . "https://github.com/mkcms/compiler-explorer.el"))]) (composable . [(20220608 1148) ((emacs (25 1))) "composable editing" tar ((:commit . "205a69c64ea95ef67070423c31ed70ec44ec980c") (:authors ("Simon Friis Vindum" . "simon@vindum.io")) (:maintainers ("Simon Friis Vindum" . "simon@vindum.io")) (:maintainer "Simon Friis Vindum" . "simon@vindum.io") (:keywords "lisp"))]) (composer . [(20221120 202) ((emacs (25 1)) (seq (1 9)) (php-runtime (0 1 0))) "Interface to PHP Composer" tar ((:commit . "2299cd731205906350d615021f99a66d7a8905c2") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainers ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "tools" "php" "dependency" "manager") (:url . "https://github.com/zonuexe/composer.el"))]) (comware-router-mode . [(20240103 907) ((dash (2 16 0)) (emacs (24 3))) "Major mode for editing Comware configuration files" tar ((:commit . "e1671efe5e0ade2dcbea0c17697d460cd8f0ba67") (:authors ("Davide Restivo" . "davide.restivo@yahoo.it")) (:maintainers ("Davide Restivo" . "davide.restivo@yahoo.it")) (:maintainer "Davide Restivo" . "davide.restivo@yahoo.it") (:keywords "convenience" "faces") (:url . "https://github.com/daviderestivo/comware-router-mode"))]) @@ -955,7 +956,7 @@ (ddskk . [(20230701 2340) ((ccc (1 43)) (cdb (20141201 754))) "Simple Kana to Kanji conversion program." tar ((:commit . "8c47f46e38a29a0f3eabcd524268d20573102467") (:authors ("Masahiko Sato" . "masahiko@kuis.kyoto-u.ac.jp")) (:maintainer "SKK Development Team") (:keywords "japanese" "mule" "input method") (:url . "https://github.com/skk-dev/ddskk"))]) (ddskk-posframe . [(20200812 917) ((emacs (26 1)) (posframe (0 4 3)) (ddskk (16 2 50))) "Show Henkan tooltip for ddskk via posframe" tar ((:commit . "299493dd951e5a0b43b8213321e3dc0bac10f762") (:authors ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainers ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "Naoya Yamashita" . "conao3@gmail.com") (:keywords "tooltip" "convenience" "posframe") (:url . "https://github.com/conao3/ddskk-posframe.el"))]) (deadgrep . [(20240408 1537) ((emacs (25 1)) (dash (2 12 0)) (s (1 11 0)) (spinner (1 7 3))) "fast, friendly searching with ripgrep" tar ((:commit . "eafc642c551e6d5df7eb1fee9aa7596e59811178") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainers ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk") (:keywords "tools") (:url . "https://github.com/Wilfred/deadgrep"))]) - (debian-el . [(20240519 1104) nil "startup file for the debian-el package" tar ((:commit . "a76a38cf0af5625b38e1d298bbfa3ae5a2a1ebc9") (:authors ("Debian Emacsen Team" . "debian-emacsen@lists.debian.org")) (:maintainers ("Debian Emacsen Team" . "debian-emacsen@lists.debian.org")) (:maintainer "Debian Emacsen Team" . "debian-emacsen@lists.debian.org") (:keywords "debian" "apt" "elisp"))]) + (debian-el . [(20240606 2249) nil "startup file for the debian-el package" tar ((:commit . "aed8571894f44a3e2394ed201c94f138b2d24365") (:authors ("Debian Emacsen Team" . "debian-emacsen@lists.debian.org")) (:maintainers ("Debian Emacsen Team" . "debian-emacsen@lists.debian.org")) (:maintainer "Debian Emacsen Team" . "debian-emacsen@lists.debian.org") (:keywords "debian" "apt" "elisp"))]) (debpaste . [(20161214 2023) ((xml-rpc (1 6 7))) "Interface for getting/posting/deleting pastes from paste.debian.net" tar ((:commit . "6f2a400665062468ebd03a2ce1de2a73d9084958") (:authors ("Alex Kost" . "alezost@gmail.com")) (:maintainers ("Alex Kost" . "alezost@gmail.com")) (:maintainer "Alex Kost" . "alezost@gmail.com") (:keywords "paste") (:url . "http://github.com/alezost/debpaste.el"))]) (debug-print . [(20140126 19) ((emacs (24))) "A nice printf debugging environment by the way Gauche do" tar ((:commit . "d817fd9ea2d3f8d2c1ace4d8af155684f3a99dc5") (:authors ("Ken Okada" . "keno.ss57@gmail.com")) (:maintainers ("Ken Okada" . "keno.ss57@gmail.com")) (:maintainer "Ken Okada" . "keno.ss57@gmail.com") (:keywords "extensions" "lisp" "tools" "maint") (:url . "https://github.com/kenoss/debug-print"))]) (decide . [(20230424 1647) nil "rolling dice and other random things" tar ((:commit . "9c0e4c4493f9af9a981627d0630ac6cb2d8c98f0") (:authors ("Pelle Nilsson" . "perni@lysator.liu.se")) (:maintainers ("Pelle Nilsson" . "perni@lysator.liu.se")) (:maintainer "Pelle Nilsson" . "perni@lysator.liu.se"))]) @@ -980,7 +981,7 @@ (demo-it . [(20211221 2152) nil "Create demonstrations" tar ((:commit . "8ade739bb2605275f1f56128a0a9a8c6b55bab6a") (:authors ("Howard Abrams" . "howard.abrams@gmail.com")) (:maintainers ("Howard Abrams" . "howard.abrams@gmail.com")) (:maintainer "Howard Abrams" . "howard.abrams@gmail.com") (:keywords "demonstration" "presentation" "test"))]) (deno-fmt . [(20230117 1117) ((emacs (24))) "Minor mode for using deno fmt on save" tar ((:commit . "6378966f448a3b9b5ae98af58cd13a031bd26702") (:authors ("Russell Clarey <http://github/rclarey>")) (:maintainers ("Russell Clarey <http://github/rclarey>")) (:maintainer "Russell Clarey <http://github/rclarey>") (:url . "https://github.com/russell/deno-emacs"))]) (deno-ts-mode . [(20230912 202) ((emacs (29 1))) "Major mode for Deno" tar ((:commit . "526b6c00483cd86a028805e31ebd8a4a7000c3da") (:authors ("Graham Marlow" . "info@mgmarlow.com")) (:maintainers ("Graham Marlow" . "info@mgmarlow.com")) (:maintainer "Graham Marlow" . "info@mgmarlow.com") (:keywords "languages") (:url . "https://git.sr.ht/~mgmarlow/deno-ts-mode"))]) - (denote-explore . [(20240602 345) ((emacs (29 1)) (denote (2 3 5)) (dash (2 19 1))) "Explore Denote files" tar ((:commit . "1680f81d816300c5821dac0b4d197e6a4368a876") (:authors ("Peter Prevos" . "peter@prevos.net")) (:maintainers ("Peter Prevos" . "peter@prevos.net")) (:maintainer "Peter Prevos" . "peter@prevos.net") (:url . "https://github.com/pprevos/denote-explore/"))]) + (denote-explore . [(20240608 251) ((emacs (29 1)) (denote (2 3 5)) (dash (2 19 1))) "Explore Denote files" tar ((:commit . "64365d901d92fa65b70897a05a6ad99cb616c3ed") (:authors ("Peter Prevos" . "peter@prevos.net")) (:maintainers ("Peter Prevos" . "peter@prevos.net")) (:maintainer "Peter Prevos" . "peter@prevos.net") (:url . "https://github.com/pprevos/denote-explore/"))]) (derl . [(20231004 821) ((emacs (29 1))) "Erlang distribution protocol implementation" tar ((:commit . "6f31592bb3083de366cdb13a7db0ed69fc72de47") (:authors ("Axel Forsman" . "axel@axelf.se")) (:maintainers ("Axel Forsman" . "axel@axelf.se")) (:maintainer "Axel Forsman" . "axel@axelf.se") (:keywords "comm" "extensions" "languages" "processes") (:url . "https://github.com/axelf4/derl.el"))]) (describe-hash . [(20200718 1556) nil "Help function for examining a hash map" tar ((:commit . "20dbbbea630055b2401f13a55fbb21216960dc46") (:url . "https://github.com/Junker/describe-hash"))]) (describe-number . [(20151101 55) ((yabin (1 1))) "Describe arbitrarily large number at point." tar ((:commit . "40618345a37831804b29589849a785ef5aa5ac24") (:authors ("Morten Slot Kristensen <msk AT nullpointer DOT dk>")) (:maintainers ("Morten Slot Kristensen <msk AT nullpointer DOT dk>")) (:maintainer "Morten Slot Kristensen <msk AT nullpointer DOT dk>") (:keywords "describe" "value" "help") (:url . "https://github.com/netromdk/describe-number"))]) @@ -1050,7 +1051,7 @@ (dired-lsi . [(20200812 929) ((emacs (26 1))) "Add memo to directory and show it in dired" tar ((:commit . "0f4038c8b47f6cfc70f82062800700c14c9912c2") (:authors ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainers ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "Naoya Yamashita" . "conao3@gmail.com") (:keywords "convenience") (:url . "https://github.com/conao3/dired-lsi.el"))]) (dired-narrow . [(20230512 1107) ((dash (2 7 0)) (dired-hacks-utils (0 0 1))) "Live-narrowing of search results for dired" tar ((:commit . "523f51b4152a3bf4e60fe57f512732c698b5c96f") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainers ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:keywords "files"))]) (dired-open . [(20240330 1831) ((dash (2 5 0)) (dired-hacks-utils (0 0 1))) "Open files from dired using using custom actions" tar ((:commit . "a01c126c3b1068655509487c76971895f5459d09") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainers ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:keywords "files"))]) - (dired-open-with . [(20240527 1855) ((emacs (27 1)) (s (1 13 0))) "And \"Open with\" dialog for Dired" tar ((:commit . "e75bc673d11ecc1385e53c378c8f31d85d157113") (:authors ("Jakub Kadlčík" . "frostyx@email.cz")) (:maintainers ("Jakub Kadlčík" . "frostyx@email.cz")) (:maintainer "Jakub Kadlčík" . "frostyx@email.cz") (:keywords "files" "dired" "xdg" "open-with") (:url . "https://github.com/FrostyX/dired-open-with"))]) + (dired-open-with . [(20240605 832) ((emacs (27 1))) "And \"Open with\" dialog for Dired" tar ((:commit . "7bc5d9dd317b7a9300b8ffc409edbc7bacb0bf08") (:authors ("Jakub Kadlčík" . "frostyx@email.cz")) (:maintainers ("Jakub Kadlčík" . "frostyx@email.cz")) (:maintainer "Jakub Kadlčík" . "frostyx@email.cz") (:keywords "files" "dired" "xdg" "open-with") (:url . "https://github.com/FrostyX/dired-open-with"))]) (dired-posframe . [(20200817 420) ((emacs (26 1)) (posframe (0 7))) "Peep dired items using posframe" tar ((:commit . "1a21eb9ad956a0371dd3c9e1bec53407d685f705") (:authors ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainers ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "Naoya Yamashita" . "conao3@gmail.com") (:keywords "convenience") (:url . "https://github.com/conao3/dired-posframe.el"))]) (dired-quick-sort . [(20240411 229) ((hydra (0 13 0)) (emacs (24))) "Persistent quick sorting of dired buffers in various ways." tar ((:commit . "d50b910baa716dfcf4830f1c08226c92bcaee7f6") (:authors ("Hong Xu" . "hong@topbug.net")) (:maintainers ("Hong Xu" . "hong@topbug.net")) (:maintainer "Hong Xu" . "hong@topbug.net") (:keywords "convenience" "files") (:url . "https://gitlab.com/xuhdev/dired-quick-sort#dired-quick-sort"))]) (dired-rainbow . [(20221127 1247) ((dash (2 5 0)) (dired-hacks-utils (0 0 1))) "Extended file highlighting according to its type" tar ((:commit . "41d3eb42195d9f0894c20d18cc8e722b099aa1c1") (:authors ("Matus Goljer" . "matus.goljer@gmail.com")) (:maintainers ("Matus Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matus Goljer" . "matus.goljer@gmail.com") (:keywords "files"))]) @@ -1075,7 +1076,7 @@ (dirtree . [(20140129 832) ((tree-mode (1 1 1 1)) (windata (0))) "Directory tree views" tar ((:commit . "ba55f1e716e386fdd37cb8e7f48616e405dc7251") (:authors ("Ye Wenbin" . "wenbinye@gmail.com")) (:maintainers ("Ye Wenbin" . "wenbinye@gmail.com")) (:maintainer "Ye Wenbin" . "wenbinye@gmail.com"))]) (dirtree-prosjekt . [(20140129 904) ((prosjekt (0 3)) (dirtree (0 1))) "dirtree integration for prosjekt." tar ((:commit . "03e06910589ba5cd736868793eb436b3233c6a26") (:authors ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainers ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainer "Austin Bingham" . "austin.bingham@gmail.com") (:url . "https://github.com/abingham/prosjekt"))]) (dirvish . [(20230519 1500) ((emacs (27 1)) (transient (0 3 7))) "A modern file manager based on dired mode" tar ((:commit . "119f9f59a618bb7b476c93e9ab1d7542c5c1df41") (:authors ("Alex Lu <https://github.com/alexluigit>")) (:maintainers ("Alex Lu <https://github.com/alexluigit>")) (:maintainer "Alex Lu <https://github.com/alexluigit>") (:keywords "files" "convenience") (:url . "https://github.com/alexluigit/dirvish"))]) - (disable-mouse . [(20210512 2114) ((emacs (24 1))) "Disable mouse commands globally" tar ((:commit . "cae3be9dd012727b40ad3b511731191f79cebe42") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainers ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "mouse") (:url . "https://github.com/purcell/disable-mouse"))]) + (disable-mouse . [(20240604 900) ((emacs (24 1))) "Disable mouse commands globally" tar ((:commit . "93a55a6453f34049375f97d3cf817b4e6db46f25") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainers ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:keywords "mouse") (:url . "https://github.com/purcell/disable-mouse"))]) (disaster . [(20230311 2324) ((emacs (27))) "Disassemble C, C++ or Fortran code under cursor" tar ((:commit . "16bba9afb92aacf06c088c29ba47813b65a80d87") (:authors ("Justine Tunney" . "jtunney@gmail.com") ("Abdelhak Bougouffa" . "abougouffa@fedoraproject.org")) (:maintainers ("Abdelhak Bougouffa" . "abougouffa@fedoraproject.org")) (:maintainer "Abdelhak Bougouffa" . "abougouffa@fedoraproject.org") (:keywords "tools" "c") (:url . "https://github.com/jart/disaster"))]) (discourse . [(20160911 819) ((cl-lib (0 5)) (request (0 2)) (s (1 11 0))) "discourse api" tar ((:commit . "a86c7e608851e186fe12e892a573994f08c8e65e") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainers ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "lisp" "discourse") (:url . "https://github.com/lujun9972/discourse-api"))]) (discover . [(20140103 2139) ((makey (0 3))) "discover more of Emacs" tar ((:commit . "bbfda2b4e429985a8fa7971d264c942767cfa816") (:authors ("Mickey Petersen" . "mickey@fyeah.org")) (:maintainers ("Mickey Petersen" . "mickey@fyeah.org")) (:maintainer "Mickey Petersen" . "mickey@fyeah.org"))]) @@ -1127,7 +1128,7 @@ (dokuwiki-mode . [(20170223 1301) nil "Major mode for DokuWiki document" tar ((:commit . "e4e116f6fcc373e3f5937c1a7daa5c2c9c6d3fa1") (:authors ("Tsunenobu Kai" . "kai2nenobu@gmail.com")) (:maintainers ("Tsunenobu Kai" . "kai2nenobu@gmail.com")) (:maintainer "Tsunenobu Kai" . "kai2nenobu@gmail.com") (:keywords "hypermedia" "text" "dokuwiki") (:url . "https://github.com/kai2nenobu/emacs-dokuwiki-mode"))]) (dollaro . [(20151123 1302) ((s (1 6 0))) "simple text templates" tar ((:commit . "500127f0172ac7a1eec627e026b59136580a74ac") (:authors ("Alessandro Piras" . "laynor@gmail.com")) (:maintainers ("Alessandro Piras" . "laynor@gmail.com")) (:maintainer "Alessandro Piras" . "laynor@gmail.com") (:keywords "tools" "convenience"))]) (doom . [(20180301 2308) ((cl-lib (0 5))) "DOM implementation and manipulation library" tar ((:commit . "e59040aefc92dd9b3134eb623624307fb9e4327b") (:authors ("Alex Schroeder" . "alex@gnu.org") ("Henrik.Motakef" . "elisp@henrik-motakef.de") ("Katherine Whitlock" . "toroidal-code@gmail.com") ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainers ("Alex Schroeder")) (:maintainer "Alex Schroeder") (:keywords "xml" "dom") (:url . "http://www.github.com/kensanata/doom.el/"))]) - (doom-modeline . [(20240510 144) ((emacs (25 1)) (compat (29 1 4 5)) (nerd-icons (0 1 0)) (shrink-path (0 3 1))) "A minimal and modern mode-line" tar ((:commit . "65d0bd83eb7c393092e032c24b882f3ba19b4899") (:authors ("Vincent Zhang" . "seagle0128@gmail.com")) (:maintainers ("Vincent Zhang" . "seagle0128@gmail.com")) (:maintainer "Vincent Zhang" . "seagle0128@gmail.com") (:keywords "faces" "mode-line") (:url . "https://github.com/seagle0128/doom-modeline"))]) + (doom-modeline . [(20240605 628) ((emacs (25 1)) (compat (29 1 4 5)) (nerd-icons (0 1 0)) (shrink-path (0 3 1))) "A minimal and modern mode-line" tar ((:commit . "11ae6c193cd9cb8d7ff7996058e6df2c0d1e408b") (:authors ("Vincent Zhang" . "seagle0128@gmail.com")) (:maintainers ("Vincent Zhang" . "seagle0128@gmail.com")) (:maintainer "Vincent Zhang" . "seagle0128@gmail.com") (:keywords "faces" "mode-line") (:url . "https://github.com/seagle0128/doom-modeline"))]) (doom-modeline-now-playing . [(20240522 1704) ((emacs (24 4)) (doom-modeline (3 0 0)) (async (1 9 3))) "Segment for Doom Modeline to show playerctl information" tar ((:commit . "1532f324f98a234aa14e12ebdfd17cebba978d6a") (:authors ("Ellis Kenyő" . "me@elken.dev")) (:maintainers ("Ellis Kenyő" . "me@elken.dev")) (:maintainer "Ellis Kenyő" . "me@elken.dev") (:url . "https://github.com/elken/doom-modeline-now-playing"))]) (doom-themes . [(20240404 2042) ((emacs (25 1)) (cl-lib (0 5))) "an opinionated pack of modern color-themes" tar ((:commit . "3b2422b208d28e8734b300cd3cc6a7f4af5eba55") (:authors ("Henrik Lissner" . "contact@henrik.io")) (:maintainers ("Henrik Lissner" . "contact@henrik.io")) (:maintainer "Henrik Lissner" . "contact@henrik.io") (:keywords "themes" "faces") (:url . "https://github.com/doomemacs/themes"))]) (dot-env . [(20230820 2014) ((emacs (24 4)) (s (1 13 0))) "Dotenv functionality" tar ((:commit . "83ce690e8ef9175fc621c85d5fbef4f7ace7b7a8") (:authors ("Amo DelBello")) (:maintainers ("Amo DelBello")) (:maintainer "Amo DelBello") (:keywords "convenience" "dotenv" "environment" "configuration") (:url . "https://github.com/amodelbello/dot-env.el"))]) @@ -1140,7 +1141,7 @@ (doxy-graph-mode . [(20210604 723) ((emacs (26 3))) "Links source code editing with doxygen call graphs" tar ((:commit . "88af6ef4bc9c8918b66c7774f0a115b2addc310e") (:authors ("Gustavo Puche" . "gustavo.puche@gmail.com")) (:maintainers ("Gustavo Puche" . "gustavo.puche@gmail.com")) (:maintainer "Gustavo Puche" . "gustavo.puche@gmail.com") (:keywords "languages" "all") (:url . "https://github.com/gustavopuche/doxy-graph-mode"))]) (dpaste . [(20160303 2112) nil "Emacs integration for dpaste.com" tar ((:commit . "e7a1a18de77f752eb0dbb4b878925f2265538d0b") (:authors ("Greg Newman" . "greg@gregnewman.org") ("Guilherme Gondim" . "semente@taurinus.org")) (:maintainers ("Greg Newman" . "greg@gregnewman.org")) (:maintainer "Greg Newman" . "greg@gregnewman.org") (:keywords "paste" "pastie" "pastebin" "dpaste" "python"))]) (dpaste_de . [(20131015 1225) ((web (0 3 7))) "Emacs mode to paste to dpaste.de" tar ((:commit . "ab041443884a7a4bfdc81b055688821e8efc9b02") (:authors ("Thejaswi Puthraya" . "thejaswi.puthraya@gmail.com")) (:maintainers ("Thejaswi Puthraya" . "thejaswi.puthraya@gmail.com")) (:maintainer "Thejaswi Puthraya" . "thejaswi.puthraya@gmail.com") (:keywords "pastebin"))]) - (dpkg-dev-el . [(20240421 628) nil "startup file for the elpa-dpkg-dev-el package" tar ((:commit . "acfc3bd42271286d22d7cbadd48a5b9d5675f85c") (:authors ("Peter S Galbraith" . "psg@debian.org")) (:maintainers ("Peter S Galbraith" . "psg@debian.org")) (:maintainer "Peter S Galbraith" . "psg@debian.org"))]) + (dpkg-dev-el . [(20240609 713) ((emacs (27 1)) (debian-el (37 0))) "startup file for the elpa-dpkg-dev-el package" tar ((:commit . "28df6a839dad6b7e4105e5fde8459374a6bb9a03") (:authors ("Peter S Galbraith" . "psg@debian.org")) (:maintainers ("Peter S Galbraith" . "psg@debian.org")) (:maintainer "Peter S Galbraith" . "psg@debian.org"))]) (dr-racket-like-unicode . [(20220810 2000) ((emacs (24 3))) "DrRacket-style unicode input" tar ((:commit . "d09b9be289e91e25c941107be5e8f52e7c8f0065") (:authors ("David Christiansen" . "david@davidchristiansen.dk")) (:maintainers ("David Christiansen" . "david@davidchristiansen.dk")) (:maintainer "David Christiansen" . "david@davidchristiansen.dk") (:keywords "i18n" "tools") (:url . "https://github.com/david-christiansen/dr-racket-like-unicode"))]) (dracula-theme . [(20231013 821) ((emacs (24 3))) "Dracula Theme" tar ((:commit . "29d5180f7e34c0c858a520068fb650f705b8cfc2") (:authors ("film42")) (:maintainers ("Étienne Deparis" . "etienne@depar.is")) (:maintainer "Étienne Deparis" . "etienne@depar.is") (:url . "https://github.com/dracula/emacs"))]) (draft-mode . [(20160106 859) nil "Rough drafting for Emacs." tar ((:commit . "4779fb32daf53746459da2def7e08004492d4f18") (:authors ("Eeli Reilin" . "gaudecker@fea.st")) (:maintainers ("Eeli Reilin" . "gaudecker@fea.st")) (:maintainer "Eeli Reilin" . "gaudecker@fea.st") (:keywords "draft" "drafting") (:url . "https://github.com/gaudecker/draft-mode"))]) @@ -1190,9 +1191,9 @@ (e2wm-term . [(20240107 850) ((e2wm (1 2)) (log4e (0 2 0)) (yaxception (1 0 0))) "Perspective of e2wm.el for work in terminal" tar ((:commit . "4542e52138484933dd99a497ff1b048ea42f9246") (:authors ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainers ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainer "Hiroaki Otsu" . "ootsuhiroaki@gmail.com") (:keywords "tools" "window manager") (:url . "https://github.com/aki2o/e2wm-term"))]) (eacl . [(20220526 1434) ((emacs (25 1))) "Auto-complete lines by grepping project" tar ((:commit . "4fe2cafbfeb73d806ebea8801c3522ff2886f30b") (:authors ("Chen Bin <chenbin DOT sh AT gmail DOT com>")) (:maintainers ("Chen Bin <chenbin DOT sh AT gmail DOT com>")) (:maintainer "Chen Bin <chenbin DOT sh AT gmail DOT com>") (:keywords "abbrev" "convenience" "matching") (:url . "http://github.com/redguardtoo/eacl"))]) (earthfile-mode . [(20230809 2250) ((emacs (26))) "Major mode for editing Earthly file" tar ((:commit . "3029e5ab06171ca5947041e95053561e10e5ba41") (:authors ("Thanabodee Charoenpiriyakij" . "wingyminus@gmail.com")) (:maintainers ("Thanabodee Charoenpiriyakij" . "wingyminus@gmail.com")) (:maintainer "Thanabodee Charoenpiriyakij" . "wingyminus@gmail.com") (:url . "https://github.com/earthly/earthly-mode"))]) - (eask . [(20240420 44) ((emacs (26 1))) "Core Eask APIs, for Eask CLI development" tar ((:commit . "ecee5a60b9e13796400e9dec84ce55f89767b6fa") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "lisp" "eask" "api") (:url . "https://github.com/emacs-eask/eask"))]) + (eask . [(20240607 515) ((emacs (26 1))) "Core Eask APIs, for Eask CLI development" tar ((:commit . "208014fc5ca345d4b7c979169489cb619c744bde") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "lisp" "eask" "api") (:url . "https://github.com/emacs-eask/eask"))]) (eask-mode . [(20240101 819) ((emacs (24 3)) (eask (0 1 0))) "Major mode for editing Eask files" tar ((:commit . "774bf05f2d778a107f27f8fa47034ad15f16395c") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "lisp" "eask") (:url . "https://github.com/emacs-eask/eask-mode"))]) - (easky . [(20240420 113) ((emacs (27 1)) (eask-mode (0 1 0)) (eask (0 1 0)) (ansi (0 4 1)) (lv (0 0)) (marquee-header (0 1 0))) "Control the Eask command-line interface" tar ((:commit . "bde4a0af084f356b993b5fd5b727c05c54e1d132") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "maint" "easky") (:url . "https://github.com/emacs-eask/easky"))]) + (easky . [(20240608 744) ((emacs (27 1)) (eask-mode (0 1 0)) (eask (0 1 0)) (ansi (0 4 1)) (lv (0 0)) (marquee-header (0 1 0))) "Control the Eask command-line interface" tar ((:commit . "d75ec4865742a4939bd685360f8ec5b076bdcf77") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "maint" "easky") (:url . "https://github.com/emacs-eask/easky"))]) (easy-after-load . [(20170817 1231) nil "eval-after-load for all files in a directory" tar ((:commit . "29e20145da49ac9ea40463c552130777408040de") (:authors ("Kyle Hargraves")) (:maintainers ("Kyle Hargraves")) (:maintainer "Kyle Hargraves") (:url . "https://github.com/pd/easy-after-load"))]) (easy-escape . [(20210917 1254) nil "Improve readability of escape characters in regular expressions" tar ((:commit . "938497a21e65ba6b3ff8ec90e93a6d0ab18dc9b4") (:authors ("Clément Pit-Claudel" . "clement.pitclaudel@live.com")) (:maintainers ("Clément Pit-Claudel" . "clement.pitclaudel@live.com")) (:maintainer "Clément Pit-Claudel" . "clement.pitclaudel@live.com") (:keywords "convenience" "lisp" "tools") (:url . "https://github.com/cpitclaudel/easy-escape"))]) (easy-hugo . [(20240129 1534) ((emacs (25 1)) (popup (0 5 3)) (request (0 3 0)) (transient (0 3 6))) "Write blogs made with hugo by markdown or org-mode" tar ((:commit . "ecae28ef6bd70f3b7492592008bfa8776d81d2e7") (:authors ("Masashi Miyaura")) (:maintainers ("Masashi Miyaura")) (:maintainer "Masashi Miyaura") (:url . "https://github.com/masasam/emacs-easy-hugo"))]) @@ -1227,7 +1228,7 @@ (edit-list . [(20100930 1443) nil "edit a single list" tar ((:commit . "f460d3f9e208a4e606fe6ded307f1b011916ca71") (:authors ("Michael Olson" . "mwolson@gnu.org")) (:maintainers ("Michael Olson" . "mwolson@gnu.org")) (:maintainer "Michael Olson" . "mwolson@gnu.org") (:url . "http://mwolson.org/static/dist/elisp/edit-list.el"))]) (edit-server . [(20220908 1014) nil "server that responds to edit requests from Chrome" tar ((:commit . "3ce09c6eb2919d56ef052b1584bba6abb12f7e99") (:authors ("Alex Bennée" . "alex@bennee.com")) (:maintainers ("Alex Bennée" . "alex@bennee.com")) (:maintainer "Alex Bennée" . "alex@bennee.com") (:url . "https://github.com/stsquad/emacs_chrome"))]) (edit-server-htmlize . [(20130329 2248) ((edit-server (1 9))) "(de)HTMLization hooks for edit-server.el" tar ((:commit . "e7f8dadfabe869c77ca241cd6fbd4c52bd908392") (:authors ("Roland McGrath" . "roland@hack.frob.com")) (:maintainers ("Roland McGrath" . "roland@hack.frob.com")) (:maintainer "Roland McGrath" . "roland@hack.frob.com") (:url . "https://github.com/frobtech/edit-server-htmlize"))]) - (editorconfig . [(20240531 1319) ((emacs (26 1)) (nadvice (0 3))) "EditorConfig Emacs Plugin" tar ((:commit . "150f8d711203b91a7ddac3ed79a65289b0f87364") (:authors ("EditorConfig Team" . "editorconfig@googlegroups.com")) (:maintainers ("EditorConfig Team" . "editorconfig@googlegroups.com")) (:maintainer "EditorConfig Team" . "editorconfig@googlegroups.com") (:keywords "convenience" "editorconfig") (:url . "https://github.com/editorconfig/editorconfig-emacs#readme"))]) + (editorconfig . [(20240604 602) ((emacs (26 1)) (nadvice (0 3))) "EditorConfig Emacs Plugin" tar ((:commit . "0ce1abc65bfb030ccec97b0d4231667ca431e663") (:authors ("EditorConfig Team" . "editorconfig@googlegroups.com")) (:maintainers ("EditorConfig Team" . "editorconfig@googlegroups.com")) (:maintainer "EditorConfig Team" . "editorconfig@googlegroups.com") (:keywords "convenience" "editorconfig") (:url . "https://github.com/editorconfig/editorconfig-emacs#readme"))]) (editorconfig-charset-extras . [(20180223 457) ((editorconfig (0 6 0))) "Extra EditorConfig Charset Support" tar ((:commit . "ddf60923c6f4841cb593b2ea04c9c710a01d262f") (:authors ("10sr" . "8.slashes@gmail.com")) (:maintainers ("10sr" . "8.slashes@gmail.com")) (:maintainer "10sr" . "8.slashes@gmail.com") (:keywords "tools") (:url . "https://github.com/10sr/editorconfig-charset-extras-el"))]) (editorconfig-custom-majormode . [(20180816 244) ((editorconfig (0 6 0))) "Decide major-mode and mmm-mode from EditorConfig" tar ((:commit . "13ad1c83f847bedd4b3a19f9df7fd925853b19de") (:authors ("10sr <8slashes+el [at] gmail [dot] com>")) (:maintainers ("10sr <8slashes+el [at] gmail [dot] com>")) (:maintainer "10sr <8slashes+el [at] gmail [dot] com>") (:keywords "editorconfig" "util") (:url . "https://github.com/10sr/editorconfig-custom-majormode-el"))]) (editorconfig-domain-specific . [(20180505 924) ((cl-lib (0 5)) (editorconfig (0 6 0))) "Apply brace style and other \"domain-specific\" EditorConfig properties" tar ((:commit . "e9824160fb2e466afa755240ee3ab7cc5657fb04") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainers ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "editorconfig" "util") (:url . "https://github.com/lassik/editorconfig-emacs-domain-specific"))]) @@ -1281,8 +1282,8 @@ (elcontext . [(20210109 1238) ((ht (2 3)) (hydra (0 14 0)) (emacs (24 3)) (f (0 20 0)) (osx-location (0 4)) (uuidgen (0 3))) "Create context specific actions" tar ((:commit . "2efd3dd8c5176c4f071bb048be6cb069b05d6e9e") (:authors ("Thomas Sojka")) (:maintainers ("Thomas Sojka")) (:maintainer "Thomas Sojka") (:keywords "calendar" "convenience") (:url . "https://github.com/rollacaster/elcontext"))]) (elcord . [(20240305 2138) ((emacs (25 1))) "Allows you to integrate Rich Presence from Discord" tar ((:commit . "e2775f40ec55dfdceea83d535dff77d60534b6bc") (:authors ("heatingdevice") ("Wilfredo Velázquez-Rodríguez" . "zulu.inuoe@gmail.com")) (:maintainers ("heatingdevice")) (:maintainer "heatingdevice") (:keywords "games") (:url . "https://github.com/Mstrodl/elcord"))]) (elcouch . [(20230903 750) ((emacs (25 1)) (json-mode (1 0 0)) (libelcouch (0 11 0)) (navigel (0 3 0))) "View and manipulate CouchDB databases" tar ((:commit . "a426e9bee9501284f4e1e84766621ca6b130c79a") (:authors ("Damien Cassou" . "damien@cassou.me")) (:maintainers ("Damien Cassou" . "damien@cassou.me")) (:maintainer "Damien Cassou" . "damien@cassou.me") (:keywords "data" "tools") (:url . "https://gitlab.petton.fr/DamienCassou/elcouch"))]) - (eldev . [(20240601 1046) ((emacs (24 4))) "Elisp development tool" tar ((:commit . "b94d85361922297e374edc05d55f249111eb84ec") (:authors ("Paul Pogonyshev" . "pogonyshev@gmail.com")) (:maintainers ("Paul Pogonyshev" . "pogonyshev@gmail.com")) (:maintainer "Paul Pogonyshev" . "pogonyshev@gmail.com") (:keywords "maint" "tools") (:url . "https://github.com/emacs-eldev/eldev"))]) - (eldoc-box . [(20240601 1539) ((emacs (27 1))) "Display documentation in childframe" tar ((:commit . "0be491c30e2f97da6bd680174a3223847eae567a") (:authors ("Yuan Fu" . "casouri@gmail.com")) (:maintainers ("Yuan Fu" . "casouri@gmail.com")) (:maintainer "Yuan Fu" . "casouri@gmail.com") (:url . "https://github.com/casouri/eldoc-box"))]) + (eldev . [(20240609 1211) ((emacs (24 4))) "Elisp development tool" tar ((:commit . "bb1938237ee85e477243cd45421330403df29390") (:authors ("Paul Pogonyshev" . "pogonyshev@gmail.com")) (:maintainers ("Paul Pogonyshev" . "pogonyshev@gmail.com")) (:maintainer "Paul Pogonyshev" . "pogonyshev@gmail.com") (:keywords "maint" "tools") (:url . "https://github.com/emacs-eldev/eldev"))]) + (eldoc-box . [(20240605 1742) ((emacs (27 1))) "Display documentation in childframe" tar ((:commit . "9658ba7d4616e97f2feeda3abf4aab3e96c91f28") (:authors ("Yuan Fu" . "casouri@gmail.com")) (:maintainers ("Yuan Fu" . "casouri@gmail.com")) (:maintainer "Yuan Fu" . "casouri@gmail.com") (:url . "https://github.com/casouri/eldoc-box"))]) (eldoc-cmake . [(20190419 2244) ((emacs (25 1))) "Eldoc support for CMake" tar ((:commit . "4453c03b5c95ff32842f13db2fc317fb0fe2f79e") (:authors ("Kirill Ignatiev")) (:maintainers ("Kirill Ignatiev")) (:maintainer "Kirill Ignatiev") (:url . "https://github.com/ikirill/eldoc-cmake"))]) (eldoc-eask . [(20240101 819) ((emacs (26 1)) (eask (0 1 0))) "Eldoc support for Eask-file" tar ((:commit . "ade0f239814f3b8bc77229e903d2c4b806ded90a") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience") (:url . "https://github.com/emacs-eask/eldoc-eask"))]) (eldoc-eval . [(20220106 1951) nil "Enable eldoc support when minibuffer is in use." tar ((:commit . "e91800503c90cb75dc70abe42f1d6ae499346cc1") (:authors ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainers ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainer "Thierry Volpiatto" . "thievol@posteo.net"))]) @@ -1307,7 +1308,7 @@ (elfeed-protocol . [(20231007 1535) ((emacs (24 4)) (elfeed (2 1 1)) (cl-lib (0 5))) "Provide fever/newsblur/owncloud/ttrss protocols for elfeed" tar ((:commit . "bcefb85a1d4075f36e73a94bda569e71f28a52c2") (:authors ("Xu Fasheng <fasheng[AT]fasheng.info>")) (:maintainers ("Xu Fasheng <fasheng[AT]fasheng.info>")) (:maintainer "Xu Fasheng <fasheng[AT]fasheng.info>") (:keywords "news") (:url . "https://github.com/fasheng/elfeed-protocol"))]) (elfeed-score . [(20230728 1433) ((emacs (26 1)) (elfeed (3 3 0))) "Gnus-style scoring for Elfeed" tar ((:commit . "cc1a05a95bff953eb28151056ce3ce14ba3e901d") (:authors ("Michael Herstine" . "sp1ff@pobox.com")) (:maintainer "Michael Herstine" . "sp1ff@pobox.com") (:keywords "news") (:url . "https://github.com/sp1ff/elfeed-score"))]) (elfeed-summary . [(20231231 1456) ((emacs (27 1)) (magit-section (3 3 0)) (elfeed (3 4 1))) "Feed summary interface for elfeed" tar ((:commit . "7e308adaa351f8c7f6ba839cbcfd4e3cd145401c") (:authors ("Korytov Pavel" . "thexcloud@gmail.com")) (:maintainers ("Korytov Pavel" . "thexcloud@gmail.com")) (:maintainer "Korytov Pavel" . "thexcloud@gmail.com") (:url . "https://github.com/SqrtMinusOne/elfeed-summary.el"))]) - (elfeed-tube . [(20240511 418) ((emacs (27 1)) (elfeed (3 4 1)) (aio (1 0))) "YouTube integration for Elfeed" tar ((:commit . "1f5ad2cc26d6290eb77dd36716e5887cb2cd617c") (:authors ("Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com")) (:maintainers ("Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com")) (:maintainer "Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com") (:keywords "news" "hypermedia" "convenience") (:url . "https://github.com/karthink/elfeed-tube"))]) + (elfeed-tube . [(20240606 241) ((emacs (27 1)) (elfeed (3 4 1)) (aio (1 0))) "YouTube integration for Elfeed" tar ((:commit . "0c3fbc21259e1fa794f3179a53b410ba610231f2") (:authors ("Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com")) (:maintainers ("Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com")) (:maintainer "Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com") (:keywords "news" "hypermedia" "convenience") (:url . "https://github.com/karthink/elfeed-tube"))]) (elfeed-tube-mpv . [(20230607 717) ((emacs (27 1)) (elfeed-tube (0 10)) (mpv (0 2 0))) "Control mpv from Elfeed" tar ((:commit . "6d5a24cfd0655068afd364cded5521a4550a8adb") (:authors ("Karthik Chikmagalur" . "karthikchikmagalur@gmail.com")) (:maintainers ("Karthik Chikmagalur" . "karthikchikmagalur@gmail.com")) (:maintainer "Karthik Chikmagalur" . "karthikchikmagalur@gmail.com") (:keywords "news" "hypermedia") (:url . "https://github.com/karthink/elfeed-tube"))]) (elfeed-web . [(20210226 258) ((simple-httpd (1 5 1)) (elfeed (3 2 0)) (emacs (24 3))) "web interface to Elfeed" tar ((:commit . "0ccd59aaace34546017a1a0d7c393749747d5bc6") (:authors ("Christopher Wellons" . "wellons@nullprogram.com")) (:maintainer "Christopher Wellons" . "wellons@nullprogram.com") (:url . "https://github.com/skeeto/elfeed"))]) (elfeed-webkit . [(20230604 2111) ((emacs (26 1)) (elfeed (3 4 1))) "Render elfeed entries in embedded webkit widgets" tar ((:commit . "db7ee83f9c0e67f01960b1e0489717cf7a8fd2c2") (:authors ("Fritz Grabo" . "hello@fritzgrabo.com")) (:maintainers ("Fritz Grabo" . "hello@fritzgrabo.com")) (:maintainer "Fritz Grabo" . "hello@fritzgrabo.com") (:keywords "comm") (:url . "https://github.com/fritzgrabo/elfeed-webkit"))]) @@ -1315,7 +1316,7 @@ (elgrep . [(20230814 1215) ((emacs (26 2)) (async (1 5))) "Searching files for regular expressions" tar ((:commit . "329eaf2e9e994e5535c7f7fe2685ec21d8323384") (:authors ("Tobias Zawada" . "i@tn-home.de")) (:maintainers ("Tobias Zawada" . "i@tn-home.de")) (:maintainer "Tobias Zawada" . "i@tn-home.de") (:keywords "tools" "matching" "files" "unix") (:url . "https://github.com/TobiasZawada/elgrep"))]) (elhome . [(20161025 2042) ((initsplit (20120630))) "A framework for a \"home\" Emacs configuration" tar ((:commit . "e789e806469af3e9705f72298683c21f6c3a516d") (:authors ("Dave Abrahams" . "dave@boostpro.com")) (:maintainers ("Demyan Rogozhin" . "demyan.rogozhin@gmail.com")) (:maintainer "Demyan Rogozhin" . "demyan.rogozhin@gmail.com") (:keywords "lisp") (:url . "http://github.com/demyanrogozhin/elhome"))]) (elisa . [(20240401 1528) ((emacs (29 2)) (ellama (0 8 6)) (llm (0 9 1)) (async (1 9 8))) "Emacs Lisp Information System Assistant" tar ((:commit . "c03baded1e6b1bb6b37f8df83a0d1af4cdbaf860") (:authors ("Sergey Kostyaev" . "sskostyaev@gmail.com")) (:maintainers ("Sergey Kostyaev" . "sskostyaev@gmail.com")) (:maintainer "Sergey Kostyaev" . "sskostyaev@gmail.com") (:keywords "help" "local" "tools") (:url . "http://github.com/s-kostyaev/elisa"))]) - (elisp-autofmt . [(20240421 854) ((emacs (29 1))) "Emacs lisp auto-format" tar ((:commit . "0560fe21d1173b2221fd8c600fab818f7eecbad4") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainers ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-elisp-autofmt"))]) + (elisp-autofmt . [(20240609 924) ((emacs (29 1))) "Emacs lisp auto-format" tar ((:commit . "3bf3e6853b19f825da3028ebf0a4f35377698565") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainers ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-elisp-autofmt"))]) (elisp-def . [(20230901 2308) ((dash (2 12 0)) (f (0 19 0)) (s (1 11 0)) (emacs (24 3))) "macro-aware go-to-definition for elisp" tar ((:commit . "1ad4baccbf3d0d13e7607d332ae6bc60a5dd7360") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainers ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk") (:keywords "lisp"))]) (elisp-demos . [(20240128 810) ((emacs (26 3))) "Elisp API Demos" tar ((:commit . "1a108d1c5011f9ced58be2ca98bea1fbd4130a2f") (:authors ("Xu Chunyang")) (:maintainers ("Xu Chunyang")) (:maintainer "Xu Chunyang") (:keywords "lisp" "docs") (:url . "https://github.com/xuchunyang/elisp-demos"))]) (elisp-depend . [(20190325 1114) nil "Parse depend libraries of elisp file." tar ((:commit . "6679da9a6be5a845bb4804224c8394a9bc62168f"))]) @@ -1329,7 +1330,7 @@ (elixir-mode . [(20230626 1738) ((emacs (25))) "Major mode for editing Elixir files" tar ((:commit . "00d6580a040a750e019218f9392cf9a4c2dac23a") (:keywords "languages" "elixir") (:url . "https://github.com/elixir-editors/emacs-elixir"))]) (elixir-ts-mode . [(20240116 645) ((emacs (29 1)) (heex-ts-mode (1 3))) "Major mode for Elixir with tree-sitter support" tar ((:commit . "6db05baed9a34d01edf0bfdd804d951dedc6dccb") (:authors ("Wilhelm H Kirschbaum")) (:maintainers ("Wilhelm H Kirschbaum")) (:maintainer "Wilhelm H Kirschbaum") (:keywords "elixir" "languages" "tree-sitter") (:url . "https://github.com/wkirschbaum/elixir-ts-mode"))]) (elixir-yasnippets . [(20150417 1239) ((yasnippet (0 8 0))) "Yasnippets for Elixir" tar ((:commit . "980ca7626c14ef0573bec0035ec7942796062783") (:authors ("Yinghai Zhao" . "zyinghai@gmail.com")) (:maintainer "Yinghai Zhao" . "zyinghai@gmail.com") (:keywords "snippets"))]) - (ellama . [(20240602 1504) ((emacs (28 1)) (llm (0 6 0)) (spinner (1 7 4))) "Tool for interacting with LLMs" tar ((:commit . "ef2f43b45d42a086938322c73387833b2acdf94f") (:authors ("Sergey Kostyaev" . "sskostyaev@gmail.com")) (:maintainers ("Sergey Kostyaev" . "sskostyaev@gmail.com")) (:maintainer "Sergey Kostyaev" . "sskostyaev@gmail.com") (:keywords "help" "local" "tools") (:url . "http://github.com/s-kostyaev/ellama"))]) + (ellama . [(20240609 1231) ((emacs (28 1)) (llm (0 6 0)) (spinner (1 7 4))) "Tool for interacting with LLMs" tar ((:commit . "3d6192ea09cfbd2e3d3e13dcada5f543bd79b968") (:authors ("Sergey Kostyaev" . "sskostyaev@gmail.com")) (:maintainers ("Sergey Kostyaev" . "sskostyaev@gmail.com")) (:maintainer "Sergey Kostyaev" . "sskostyaev@gmail.com") (:keywords "help" "local" "tools") (:url . "http://github.com/s-kostyaev/ellama"))]) (ellocate . [(20200112 1931) ((emacs (25 1)) (s (1 12 0)) (f (0 20 0))) "The locate command reimplemented in Emacs Lisp" tar ((:commit . "81405082f68f0577c9f176d3d4f034a7142aba59") (:authors ("Sebastian Wålinder" . "s.walinder@gmail.com")) (:maintainers ("Sebastian Wålinder" . "s.walinder@gmail.com")) (:maintainer "Sebastian Wålinder" . "s.walinder@gmail.com") (:keywords "matching") (:url . "https://github.com/walseb/ellocate"))]) (elm-mode . [(20230315 1122) ((f (0 17)) (s (1 7 0)) (emacs (25 1)) (seq (2 23)) (reformatter (0 3))) "Major mode for Elm" tar ((:commit . "699841865e1bd5b7f2077baa7121510b6bcad3c7") (:authors ("Joseph Collard")) (:maintainers ("Joseph Collard")) (:maintainer "Joseph Collard") (:url . "https://github.com/jcollard/elm-mode"))]) (elm-test-runner . [(20230905 331) ((emacs (24 4))) "Enhanced support for running elm-test" tar ((:commit . "b664e50a4c849f5f2e2f434fc01718da10515612") (:authors ("Juan Edi")) (:maintainers ("Juan Edi")) (:maintainer "Juan Edi") (:url . "https://github.com/juanedi/elm-test-runner"))]) @@ -1376,7 +1377,7 @@ (emamux . [(20200315 1220) ((emacs (24 3))) "Interact with tmux" tar ((:commit . "6172131d78038f0b1490e24bac60534bf4ad3b30") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainers ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-emamux"))]) (emamux-ruby-test . [(20130812 1639) ((emamux (0 1)) (projectile (0 9 1))) "Ruby test with emamux" tar ((:commit . "785bfd44d097a46bb2ebe1e62ac7595fd4dc9ab5") (:url . "https://github.com/syohex/emamux-ruby-test"))]) (emaps . [(20200508 1759) ((dash (2 17 0)) (emacs (24))) "Utilities for working with keymaps" tar ((:commit . "7c561f3ded2015ed3774e5784059d6601082743e") (:authors ("Ben Moon" . "software@guiltydolphin.com")) (:maintainers ("Ben Moon" . "software@guiltydolphin.com")) (:maintainer "Ben Moon" . "software@guiltydolphin.com") (:keywords "convenience" "keyboard" "keymap" "utility") (:url . "https://github.com/GuiltyDolphin/emaps"))]) - (embark . [(20240419 452) ((emacs (27 1)) (compat (29 1 4 0))) "Conveniently act on minibuffer completions" tar ((:commit . "195add1f1ccd1059472c9df7334c97c4d155425e") (:authors ("Omar Antolín Camarena" . "omar@matem.unam.mx")) (:maintainers ("Omar Antolín Camarena" . "omar@matem.unam.mx")) (:maintainer "Omar Antolín Camarena" . "omar@matem.unam.mx") (:keywords "convenience") (:url . "https://github.com/oantolin/embark"))]) + (embark . [(20240607 2213) ((emacs (27 1)) (compat (29 1 4 0))) "Conveniently act on minibuffer completions" tar ((:commit . "9c166c4b96a0b1e85401bcc6fb95ce021e7b5013") (:authors ("Omar Antolín Camarena" . "omar@matem.unam.mx")) (:maintainers ("Omar Antolín Camarena" . "omar@matem.unam.mx")) (:maintainer "Omar Antolín Camarena" . "omar@matem.unam.mx") (:keywords "convenience") (:url . "https://github.com/oantolin/embark"))]) (embark-consult . [(20240419 452) ((emacs (27 1)) (compat (29 1 4 0)) (embark (1 0)) (consult (1 0))) "Consult integration for Embark" tar ((:commit . "195add1f1ccd1059472c9df7334c97c4d155425e") (:authors ("Omar Antolín Camarena" . "omar@matem.unam.mx")) (:maintainers ("Omar Antolín Camarena" . "omar@matem.unam.mx")) (:maintainer "Omar Antolín Camarena" . "omar@matem.unam.mx") (:keywords "convenience") (:url . "https://github.com/oantolin/embark"))]) (embark-org-roam . [(20240303 335) ((emacs (27 1)) (embark (0 23)) (org-roam (2 2 0))) "Embark export buffer for org roam nodes" tar ((:commit . "5bc9efc33e74eb47becbc2f6467141864cb6ecea") (:authors ("Bram Adams" . "bram.adams@queensu.ca")) (:maintainers ("Bram Adams" . "bram.adams@queensu.ca")) (:maintainer "Bram Adams" . "bram.adams@queensu.ca") (:keywords "outlines" "hypermedia") (:url . "https://github.com/bramadams/embark-org-roam"))]) (embark-vc . [(20230212 1920) ((emacs (27 1)) (embark (0 21 1)) (forge (0 3)) (compat (29 1 3 0))) "Embark actions for various version control integrations" tar ((:commit . "070666b0de8fc2832aa2510b9ba492565cb5e35e") (:authors ("Ellis Kenyő <https://github.com/elken>")) (:maintainers ("Ellis Kenyő" . "me@elken.dev")) (:maintainer "Ellis Kenyő" . "me@elken.dev") (:keywords "convenience" "matching" "terminals" "tools" "unix" "vc") (:url . "https://github.com/elken/embark-vc"))]) @@ -1385,7 +1386,7 @@ (embrace . [(20231027 419) ((cl-lib (0 5)) (expand-region (0 10 0))) "Add/Change/Delete pairs based on `expand-region'" tar ((:commit . "c7e748603151d7d91c237fd2d9cdf56e9f3b1ea8") (:authors ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainers ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainer "Junpeng Qiu" . "qjpchmail@gmail.com") (:keywords "extensions"))]) (emidje . [(20190209 1726) ((emacs (25)) (cider (0 17 0)) (seq (2 16)) (magit-popup (2 4 0))) "Test runner and report viewer for Midje" tar ((:commit . "7e92f053964d925c97dc8cca8d4d70a3030021db") (:authors ("Alan Ghelardi" . "alan.ghelardi@nubank.com.br")) (:maintainers ("Alan Ghelardi" . "alan.ghelardi@nubank.com.br")) (:maintainer "Alan Ghelardi" . "alan.ghelardi@nubank.com.br") (:keywords "tools") (:url . "https://github.com/nubank/emidje"))]) (emmet-mode . [(20221111 329) nil "Unofficial Emmet's support for emacs" tar ((:commit . "63b6932603184956b5ea8919036d2b307b48d7fd") (:authors ("Shin Aoyama" . "smihica@gmail.com")) (:maintainers ("Shin Aoyama" . "smihica@gmail.com")) (:maintainer "Shin Aoyama" . "smihica@gmail.com") (:keywords "convenience") (:url . "https://github.com/smihica/emmet-mode"))]) - (emms . [(20240512 1735) ((cl-lib (0 5)) (nadvice (0 3)) (seq (0))) "The Emacs Multimedia System" tar ((:commit . "c86ede13e80ad573f52e46de54fd24a97e2faa88") (:authors ("Jorgen Schäfer" . "forcer@forcix.cx")) (:maintainers ("Yoni Rabkin" . "yrk@gnu.org")) (:maintainer "Yoni Rabkin" . "yrk@gnu.org") (:keywords "emms" "mp3" "ogg" "flac" "music" "mpeg" "video" "multimedia") (:url . "https://www.gnu.org/software/emms/"))]) + (emms . [(20240607 1419) ((cl-lib (0 5)) (nadvice (0 3)) (seq (0))) "The Emacs Multimedia System" tar ((:commit . "2c328f0a4d46c008d409bbe44994588816adf221") (:authors ("Jorgen Schäfer" . "forcer@forcix.cx")) (:maintainers ("Yoni Rabkin" . "yrk@gnu.org")) (:maintainer "Yoni Rabkin" . "yrk@gnu.org") (:keywords "emms" "mp3" "ogg" "flac" "music" "mpeg" "video" "multimedia") (:url . "https://www.gnu.org/software/emms/"))]) (emms-info-mediainfo . [(20131223 1300) ((emms (0))) "Info-method for EMMS using medianfo" tar ((:commit . "bce16eae9eacd38719fea62a9755225a888da59d") (:authors ("Fabián Ezequiel Gallina" . "fgallina@gnu.org")) (:maintainers ("Fabián Ezequiel Gallina" . "fgallina@gnu.org")) (:maintainer "Fabián Ezequiel Gallina" . "fgallina@gnu.org") (:keywords "multimedia" "processes"))]) (emms-mark-ext . [(20130529 327) ((emms (3 0))) "Extra functions for emms-mark-mode and emms-tag-edit-mode" tar ((:commit . "ec68129e3e9e469e5bf160c6a1b7030e322f3541") (:authors ("Joe Bloggs" . "vapniks@yahoo.com")) (:maintainers ("Joe Bloggs" . "vapniks@yahoo.com")) (:maintainer "Joe Bloggs" . "vapniks@yahoo.com") (:keywords "convenience" "multimedia") (:url . "https://github.com/vapniks/emms-mark-ext"))]) (emms-mode-line-cycle . [(20160221 1120) ((emacs (24)) (emms (4 0))) "Display the emms mode line as a ticker" tar ((:commit . "2c2f395e484a1d345050ddd61ff5fab71a92a6bc") (:authors ("momomo5717")) (:maintainers ("momomo5717")) (:maintainer "momomo5717") (:keywords "emms" "mode-line") (:url . "https://github.com/momomo5717/emms-mode-line-cycle"))]) @@ -1409,7 +1410,7 @@ (encrypt-region . [(20220802 918) ((emacs (26 1))) "Encrypts and decrypts regions" tar ((:commit . "8ff5704bc6f4c57f935a8b7680129e599bbe474f") (:authors ("Carlton Shepherd" . "carlton@linux.com")) (:maintainers ("Carlton Shepherd" . "carlton@linux.com")) (:maintainer "Carlton Shepherd" . "carlton@linux.com") (:keywords "tools" "convenience") (:url . "https://github.com/cgshep/encrypt-region"))]) (engine-mode . [(20230619 1503) ((emacs (24 4))) "Define and query search engines" tar ((:commit . "687266bff45cda00c1de57a22ad2b03de6823c28") (:authors ("Harry R. Schwartz" . "hello@harryrschwartz.com")) (:maintainers ("Harry R. Schwartz" . "hello@harryrschwartz.com")) (:maintainer "Harry R. Schwartz" . "hello@harryrschwartz.com") (:url . "https://github.com/hrs/engine-mode"))]) (enh-ruby-mode . [(20221011 1957) ((emacs (25 1))) "Major mode for editing Ruby files" tar ((:commit . "7e76d754e1632b4fc9a024fa393c3fc837bcc86b") (:authors ("Geoff Jacobsen")) (:maintainers ("Ryan Davis")) (:maintainer "Ryan Davis") (:keywords "languages" "elisp" "ruby") (:url . "https://github.com/zenspider/Enhanced-Ruby-Mode"))]) - (enlight . [(20240601 1150) ((emacs (27 1)) (compat (29 1 4 1))) "Highly customizable startup screen" tar ((:commit . "76753736da1777c8f9ebbeb08beec15b330a5878") (:authors ("Ilya Chernyshov" . "ichernyshovvv@gmail.com")) (:maintainers ("Ilya Chernyshov" . "ichernyshovvv@gmail.com")) (:maintainer "Ilya Chernyshov" . "ichernyshovvv@gmail.com") (:keywords "startup" "screen" "tools" "dashboard") (:url . "https://github.com/ichernyshovvv/enlight"))]) + (enlight . [(20240602 2025) ((emacs (27 1)) (compat (29 1 4 1))) "Highly customizable startup screen" tar ((:commit . "5194c1a4f4c245a1ef544205d723381fac30414b") (:authors ("Ilya Chernyshov" . "ichernyshovvv@gmail.com")) (:maintainers ("Ilya Chernyshov" . "ichernyshovvv@gmail.com")) (:maintainer "Ilya Chernyshov" . "ichernyshovvv@gmail.com") (:keywords "startup" "screen" "tools" "dashboard") (:url . "https://github.com/ichernyshovvv/enlight"))]) (enlightened-theme . [(20210220 2327) nil "A theme based on enlightened" tar ((:commit . "1bfebd8f47e8a8357c9e557cf6e95d7027861e6d") (:url . "https://hg.sr.ht/~slondr/enlightened"))]) (enlive . [(20170725 1417) nil "query html document with css selectors" tar ((:commit . "604a8ca272b6889f114e2b5a13adb5b1dc4bae86") (:authors ("ZHOU Feng" . "zf.pascal@gmail.com")) (:maintainers ("ZHOU Feng" . "zf.pascal@gmail.com")) (:maintainer "ZHOU Feng" . "zf.pascal@gmail.com") (:keywords "css" "selector" "query") (:url . "http://github.com/zweifisch/enlive"))]) (eno . [(20191013 1239) ((dash (2 12 1)) (edit-at-point (1 0))) "Goto/copy/cut any word/symbol/line in view, similar to ace-jump/easymotion" tar ((:commit . "c5c6193687c0bede1ddf507c430cf8b0a6d272d9") (:authors (nil . "<e.enoson@gmail.com>")) (:maintainers (nil . "<e.enoson@gmail.com>")) (:maintainer nil . "<e.enoson@gmail.com>") (:url . "http://github.com/enoson/eno.el"))]) @@ -1420,7 +1421,7 @@ (epc . [(20140610 534) ((concurrent (0 3 1)) (ctable (0 1 2))) "A RPC stack for the Emacs Lisp" tar ((:commit . "94cd36a3bec752263ac9b1b3a9dd2def329d2af7") (:authors ("SAKURAI Masashi <m.sakurai at kiwanami.net>")) (:maintainers ("SAKURAI Masashi <m.sakurai at kiwanami.net>")) (:maintainer "SAKURAI Masashi <m.sakurai at kiwanami.net>") (:keywords "lisp" "rpc") (:url . "https://github.com/kiwanami/emacs-epc"))]) (epic . [(20170210 23) ((htmlize (1 47))) "Evernote Picker for Cocoa Emacs" tar ((:commit . "a41826c330eb0ea061d58a08cc861b0c4ac8ec4e") (:authors ("Yoshinari Nomura" . "nom@quickhack.net")) (:maintainers ("Yoshinari Nomura" . "nom@quickhack.net")) (:maintainer "Yoshinari Nomura" . "nom@quickhack.net") (:keywords "evernote" "applescript") (:url . "https://github.com/yoshinari-nomura/epic"))]) (eping . [(20201027 2149) ((emacs (25 1))) "Ping websites to check internet connectivity" tar ((:commit . "004496ee06c0b8ead4a4f49e17109e8eb32eb49d") (:authors ("Sean Hutchings" . "seanhut@yandex.com")) (:maintainers ("Sean Hutchings" . "seanhut@yandex.com")) (:maintainer "Sean Hutchings" . "seanhut@yandex.com") (:keywords "comm" "processes" "terminals" "unix") (:url . "https://github.com/sean-hut/eping"))]) - (epkg . [(20240415 1554) ((emacs (25 1)) (compat (29 1 4 1)) (closql (20230407)) (emacsql (20230409)) (llama (0 2 0))) "Browse the Emacsmirror package database" tar ((:commit . "91c3e441eaa9f85b13d5957ee82f7c440addd147") (:authors ("Jonas Bernoulli" . "emacs.epkg@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.epkg@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.epkg@jonas.bernoulli.dev") (:keywords "tools") (:url . "https://github.com/emacscollective/epkg"))]) + (epkg . [(20240603 1436) ((emacs (26 1)) (compat (29 1 4 5)) (closql (20240601)) (emacsql (20240415)) (llama (0 3 1))) "Browse the Emacsmirror package database" tar ((:commit . "c3a6599e76481e5a83edd601cdbf17f0cc2e931a") (:authors ("Jonas Bernoulli" . "emacs.epkg@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.epkg@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.epkg@jonas.bernoulli.dev") (:keywords "tools") (:url . "https://github.com/emacscollective/epkg"))]) (epkg-marginalia . [(20240415 1536) ((emacs (27 1)) (compat (29 1 4 1)) (epkg (3 3 3)) (llama (0 3 0)) (marginalia (1 2))) "Show Epkg information in completion annotations" tar ((:commit . "41bb627934e0a389e24573d2c9d088f6f6afdbcc") (:authors ("Jonas Bernoulli" . "emacs.epkg-marginalia@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.epkg-marginalia@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.epkg-marginalia@jonas.bernoulli.dev") (:keywords "tools") (:url . "https://github.com/emacscollective/epkg-marginalia"))]) (epl . [(20180205 2049) ((cl-lib (0 3))) "Emacs Package Library" tar ((:commit . "78ab7a85c08222cd15582a298a364774e3282ce6") (:authors ("Sebastian Wiesner" . "swiesner@lunaryorn.com")) (:maintainers ("Johan Andersson" . "johan.rejeep@gmail.com")) (:maintainer "Johan Andersson" . "johan.rejeep@gmail.com") (:keywords "convenience") (:url . "http://github.com/cask/epl"))]) (epm . [(20190509 443) ((emacs (24 3)) (epl (0 8))) "Emacs Package Manager" tar ((:commit . "6375ddbf93c5f25647f6ebb25b54045b3c93a5be") (:authors ("Chunyang Xu" . "xuchunyang.me@gmail.com")) (:maintainers ("Chunyang Xu" . "xuchunyang.me@gmail.com")) (:maintainer "Chunyang Xu" . "xuchunyang.me@gmail.com") (:url . "https://github.com/xuchunyang/epm"))]) @@ -1478,7 +1479,7 @@ (eshell-info-banner . [(20220728 1006) ((emacs (25 1)) (s (1))) "System information as your Eshell banner" tar ((:commit . "987e69a66276ca057798896c606e5c5d5fb9ee5c") (:authors ("Lucien Cartier-Tilet" . "lucien@phundrak.com")) (:maintainers ("Lucien Cartier-Tilet" . "lucien@phundrak.com")) (:maintainer "Lucien Cartier-Tilet" . "lucien@phundrak.com") (:url . "https://github.com/Phundrak/eshell-info-banner.el"))]) (eshell-outline . [(20201121 620) ((emacs (25 1))) "Enhanced outline-mode for Eshell" tar ((:commit . "6f917afa5b3d36764d76d7864589094647d8c3b4") (:authors ("Jamie Beardslee" . "jdb@jamzattack.xyz")) (:maintainers ("Jamie Beardslee" . "jdb@jamzattack.xyz")) (:maintainer "Jamie Beardslee" . "jdb@jamzattack.xyz") (:keywords "unix" "eshell" "outline" "convenience") (:url . "https://git.jamzattack.xyz/eshell-outline"))]) (eshell-prompt-extras . [(20231019 1405) ((emacs (25))) "Display extra information for your eshell prompt." tar ((:commit . "14eabe593e110ed6937ac3b95f7979263d716a26") (:authors ("zwild" . "judezhao@outlook.com")) (:maintainers ("Xu Chunyang" . "xuchunyang56@gmail.com")) (:maintainer "Xu Chunyang" . "xuchunyang56@gmail.com") (:keywords "eshell" "prompt") (:url . "https://github.com/zwild/eshell-prompt-extras"))]) - (eshell-syntax-highlighting . [(20240509 241) ((emacs (25 1))) "Highlight eshell commands" tar ((:commit . "1198fd658d317747eb606a50c7767fef579af324") (:authors ("Alex Kreisher" . "akreisher18@gmail.com")) (:maintainers ("Alex Kreisher" . "akreisher18@gmail.com")) (:maintainer "Alex Kreisher" . "akreisher18@gmail.com") (:keywords "convenience") (:url . "https://github.com/akreisher/eshell-syntax-highlighting"))]) + (eshell-syntax-highlighting . [(20240608 211) ((emacs (25 1))) "Highlight eshell commands" tar ((:commit . "fa1d368452ebd11727d267076ae568b892fa9cb9") (:authors ("Alex Kreisher" . "akreisher18@gmail.com")) (:maintainers ("Alex Kreisher" . "akreisher18@gmail.com")) (:maintainer "Alex Kreisher" . "akreisher18@gmail.com") (:keywords "convenience") (:url . "https://github.com/akreisher/eshell-syntax-highlighting"))]) (eshell-toggle . [(20240417 1536) ((emacs (25 1)) (dash (2 11 0))) "Show/hide eshell under active window." tar ((:commit . "222e05870c0b3f4a4d96f9bdb7065c53eb43a917") (:authors ("Dmitry Cherkassov" . "dcherkassov@gmail.com")) (:maintainers ("Dmitry Cherkassov" . "dcherkassov@gmail.com")) (:maintainer "Dmitry Cherkassov" . "dcherkassov@gmail.com") (:keywords "processes") (:url . "https://github.com/4da/eshell-toggle"))]) (eshell-up . [(20240226 1747) ((emacs (24))) "Quickly go to a specific parent directory in eshell" tar ((:commit . "1999afaa509204b780db44e99ac9648fe7d92d32") (:authors ("Peter W. V. Tran-Jørgensen" . "peter.w.v.jorgensen@gmail.com")) (:maintainers ("Peter W. V. Tran-Jørgensen" . "peter.w.v.jorgensen@gmail.com")) (:maintainer "Peter W. V. Tran-Jørgensen" . "peter.w.v.jorgensen@gmail.com") (:keywords "eshell") (:url . "https://github.com/peterwvj/eshell-up"))]) (eshell-vterm . [(20240305 1149) ((emacs (27 1)) (vterm (0 0 1))) "Vterm for visual commands in eshell" tar ((:commit . "20f4b246fa605a1533cdfbe3cb7faf31a24e3d2e") (:authors ("Illia Ostapyshyn" . "ilya.ostapyshyn@gmail.com")) (:maintainers ("Illia Ostapyshyn" . "ilya.ostapyshyn@gmail.com")) (:maintainer "Illia Ostapyshyn" . "ilya.ostapyshyn@gmail.com") (:keywords "eshell" "vterm" "terminals" "shell" "visual" "tools" "processes") (:url . "https://github.com/iostapyshyn/eshell-vterm"))]) @@ -1520,7 +1521,7 @@ (evalator-clojure . [(20160208 2148) ((cider (0 10 0)) (evalator (1 0 0))) "Clojure evaluation context for evalator via CIDER." tar ((:commit . "caa4e0a137bdfada86593128a654e16aa617ad50") (:authors ("Sean Irby")) (:maintainers ("Sean Irby" . "sean.t.irby@gmail.com")) (:maintainer "Sean Irby" . "sean.t.irby@gmail.com") (:keywords "languages" "clojure" "cider" "helm") (:url . "http://www.github.com/seanirby/evalator-clojure"))]) (eve-mode . [(20170822 2231) ((emacs (25)) (polymode (1 0)) (markdown-mode (2 0))) "Major mode for editing Eve documents." tar ((:commit . "a4661114d9c18725691b76321d72167ca5a9070a") (:authors ("Joshua Cole" . "joshuafcole@gmail.com")) (:maintainers ("Joshua Cole" . "joshuafcole@gmail.com")) (:maintainer "Joshua Cole" . "joshuafcole@gmail.com") (:keywords "languages" "wp" "tools") (:url . "https://github.com/witheve/emacs-eve-mode"))]) (everlasting-scratch . [(20240430 1713) ((emacs (25 1))) "The *scratch* that lasts forever" tar ((:commit . "1837142ae14fdfd4d634434ceff966b348826259") (:authors ("Huming Chen" . "chenhuming@gmail.com")) (:maintainers ("Huming Chen" . "chenhuming@gmail.com")) (:maintainer "Huming Chen" . "chenhuming@gmail.com") (:keywords "convenience" "tool") (:url . "https://github.com/beacoder/everlasting-scratch"))]) - (evil . [(20240531 1609) ((emacs (24 1)) (goto-chg (1 6)) (cl-lib (0 5))) "Extensible vi layer" tar ((:commit . "9fada7828a1afadbec1f84675ce3c8e9729e1cdc") (:maintainer "Tom Dalziel" . "tom.dalziel@gmail.com") (:keywords "emulations") (:url . "https://github.com/emacs-evil/evil"))]) + (evil . [(20240603 1454) ((emacs (24 1)) (goto-chg (1 6)) (cl-lib (0 5))) "Extensible vi layer" tar ((:commit . "3ba76c1c1f6e8f0389d7bebbd220eefaca796da4") (:maintainer "Tom Dalziel" . "tom.dalziel@gmail.com") (:keywords "emulations") (:url . "https://github.com/emacs-evil/evil"))]) (evil-anzu . [(20220911 1939) ((evil (1 0 0)) (anzu (0 46))) "anzu for evil-mode" tar ((:commit . "d1e98ee6976437164627542909a25c6946497899") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com") ("Fredrik Bergroth" . "fbergroth@gmail.com")) (:maintainers ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-evil-anzu"))]) (evil-args . [(20240210 504) ((evil (1 0 8))) "Motions and text objects for delimited arguments in Evil." tar ((:commit . "a8151556f63c9d45d0c44c8a7ef9e5a542f3cdc7") (:authors ("Connor Smith" . "wconnorsmith@gmail.com")) (:maintainers ("Connor Smith" . "wconnorsmith@gmail.com")) (:maintainer "Connor Smith" . "wconnorsmith@gmail.com") (:keywords "evil" "vim-emulation") (:url . "http://github.com/wcsmith/evil-args"))]) (evil-avy . [(20150908 748) ((emacs (24 1)) (cl-lib (0 5)) (avy (0 3 0)) (evil (1 2 3))) "set-based completion" tar ((:commit . "2dd955cc3ecaa7ddeb67b295298abdc6d16dd3a5") (:authors ("Yufan Lou" . "loganlyf@gmail.com")) (:maintainers ("Yufan Lou" . "loganlyf@gmail.com")) (:maintainer "Yufan Lou" . "loganlyf@gmail.com") (:keywords "point" "location" "evil" "vim") (:url . "https://github.com/louy2/evil-avy"))]) @@ -1528,7 +1529,7 @@ (evil-cleverparens . [(20240529 1025) ((evil (1 0)) (paredit (1)) (smartparens (1 6 1)) (emacs (24 4)) (dash (2 12 0))) "Evil friendly minor-mode for editing lisp." tar ((:commit . "6637717af0bdac55f97eef98433d53a10395cf77") (:authors ("Olli Piepponen" . "opieppo@gmail.com")) (:maintainers ("Olli Piepponen" . "opieppo@gmail.com")) (:maintainer "Olli Piepponen" . "opieppo@gmail.com") (:keywords "convenience" "emulations") (:url . "https://github.com/emacs-evil/evil-cleverparens"))]) (evil-colemak-basics . [(20221230 1443) ((emacs (24 3)) (evil (1 2 12)) (evil-snipe (2 0 3))) "Basic Colemak key bindings for evil-mode" tar ((:commit . "ea519b962f051cabced8aadaf6ed0134b861225c") (:authors ("Wouter Bolsterlee" . "wouter@bolsterl.ee")) (:maintainers ("Wouter Bolsterlee" . "wouter@bolsterl.ee")) (:maintainer "Wouter Bolsterlee" . "wouter@bolsterl.ee") (:keywords "convenience" "emulations" "colemak" "evil") (:url . "https://github.com/wbolster/evil-colemak-basics"))]) (evil-colemak-minimal . [(20171006 1317) ((emacs (24)) (evil (1 2 12))) "Minimal Colemak key bindings for evil-mode" tar ((:commit . "6d98b6da60f414524a0d718f76024c26dce742b3") (:authors ("Bryan Allred" . "bryan@revolvingcow.com")) (:maintainers ("Bryan Allred" . "bryan@revolvingcow.com")) (:maintainer "Bryan Allred" . "bryan@revolvingcow.com") (:keywords "colemak" "evil") (:url . "https://github.com/bmallred/evil-colemak-minimal"))]) - (evil-collection . [(20240523 120) ((emacs (26 3)) (evil (1 2 13)) (annalist (1 0))) "A set of keybindings for Evil mode" tar ((:commit . "b34369cdb5c1bcecaf02094cf7e31e43c5534e32") (:authors ("James Nguyen" . "james@jojojames.com")) (:maintainers ("James Nguyen" . "james@jojojames.com")) (:maintainer "James Nguyen" . "james@jojojames.com") (:keywords "evil" "tools") (:url . "https://github.com/emacs-evil/evil-collection"))]) + (evil-collection . [(20240608 1449) ((emacs (26 3)) (evil (1 2 13)) (annalist (1 0))) "A set of keybindings for Evil mode" tar ((:commit . "acb056b1d0d3aad2f32b1ca9c019a9a2e976f03e") (:authors ("James Nguyen" . "james@jojojames.com")) (:maintainers ("James Nguyen" . "james@jojojames.com")) (:maintainer "James Nguyen" . "james@jojojames.com") (:keywords "evil" "tools") (:url . "https://github.com/emacs-evil/evil-collection"))]) (evil-commentary . [(20230610 1006) ((evil (1 0 0))) "Comment stuff out. A port of vim-commentary." tar ((:commit . "c5945f28ce47644c828aac1f5f6ec335478d17fb") (:authors ("Quang Linh LE" . "linktohack@gmail.com")) (:maintainers ("Quang Linh LE" . "linktohack@gmail.com")) (:maintainer "Quang Linh LE" . "linktohack@gmail.com") (:keywords "evil" "comment" "commentary" "evil-commentary") (:url . "http://github.com/linktohack/evil-commentary"))]) (evil-dvorak . [(20160416 1841) ((evil (1 0 8))) "evil keybindings for that work with dvorak mode" tar ((:commit . "e7b80077d6f332452049eb3d7ea51f6c8fbf5947") (:authors ("Joshua Branson")) (:maintainer "Joshua Branson") (:keywords "dvorak" "evil" "vim"))]) (evil-easymotion . [(20200424 135) ((emacs (24)) (avy (0 3 0)) (cl-lib (0 5))) "A port of vim's easymotion to emacs" tar ((:commit . "f96c2ed38ddc07908db7c3c11bcd6285a3e8c2e9") (:authors ("PythonNut" . "pythonnut@pythonnut.com")) (:maintainers ("PythonNut" . "pythonnut@pythonnut.com")) (:maintainer "PythonNut" . "pythonnut@pythonnut.com") (:keywords "convenience" "evil") (:url . "https://github.com/pythonnut/evil-easymotion"))]) @@ -1664,7 +1665,7 @@ (fcopy . [(20150304 1403) nil "Funny Copy, set past point HERE then search copy text" tar ((:commit . "e355f6ec889d8ecbdb096019c2dc660b1cec4941") (:authors ("Masayuki Ataka" . "masayuki.ataka@gmail.com")) (:maintainers ("Masayuki Ataka" . "masayuki.ataka@gmail.com")) (:maintainer "Masayuki Ataka" . "masayuki.ataka@gmail.com") (:keywords "convenience") (:url . "https://github.com/ataka/fcopy"))]) (fd-dired . [(20210723 549) ((emacs (25))) "find-dired alternative using fd" tar ((:commit . "458464771bb220b6eb87ccfd4c985c436e57dc7e") (:authors ("Rashawn Zhang" . "namy.19@gmail.com")) (:maintainers ("Rashawn Zhang" . "namy.19@gmail.com")) (:maintainer "Rashawn Zhang" . "namy.19@gmail.com") (:keywords "tools" "fd" "find" "dired") (:url . "https://github.com/yqrashawn/fd-dired"))]) (feature-mode . [(20240401 242) nil "Major mode for editing Gherkin (i.e. Cucumber) user stories" tar ((:commit . "afd49b8a8504e5874027fc0a46283adb1fea26c0") (:authors ("Michael Klishin")) (:maintainer "Michael Klishin") (:url . "https://github.com/michaelklishin/cucumber.el"))]) - (fedi . [(20240311 1417) ((emacs (28 1)) (markdown-mode (2 5))) "Helper functions for fediverse clients" tar ((:commit . "b4996a467868b11e7f4ee9c53354131a99bc6bad") (:authors ("Marty Hiatt" . "martianhiatus@riseup.net")) (:maintainers ("Marty Hiatt" . "martianhiatus@riseup.net")) (:maintainer "Marty Hiatt" . "martianhiatus@riseup.net") (:url . "https://codeberg.org/martianh/fedi.el"))]) + (fedi . [(20240607 1333) ((emacs (28 1)) (markdown-mode (2 5))) "Helper functions for fediverse clients" tar ((:commit . "8f2cf86b96d089964570d47dbe299ab3dbf13cef") (:authors ("Marty Hiatt" . "martianhiatus@riseup.net")) (:maintainers ("Marty Hiatt" . "martianhiatus@riseup.net")) (:maintainer "Marty Hiatt" . "martianhiatus@riseup.net") (:url . "https://codeberg.org/martianh/fedi.el"))]) (feebleline . [(20190822 1401) nil "Replace modeline with a slimmer proxy" tar ((:commit . "b2f2db25cac77817bf0c49ea2cea6383556faea0") (:authors ("Benjamin Lindqvist" . "benjamin.lindqvist@gmail.com")) (:maintainers ("Benjamin Lindqvist" . "benjamin.lindqvist@gmail.com")) (:maintainer "Benjamin Lindqvist" . "benjamin.lindqvist@gmail.com") (:url . "https://github.com/tautologyclub/feebleline"))]) (feed-discovery . [(20200714 1118) ((emacs (25 1)) (dash (2 16 0))) "Discover feed url by RSS/Atom autodiscovery" tar ((:commit . "3812439c845c184eaf164d3ac8935de135259855") (:authors ("Hiroki YAMAKAWA" . "s06139@gmail.com")) (:maintainers ("Hiroki YAMAKAWA" . "s06139@gmail.com")) (:maintainer "Hiroki YAMAKAWA" . "s06139@gmail.com") (:url . "https://github.com/HKey/feed-discovery"))]) (feline . [(20230301 1350) ((emacs (28 1))) "A modeline with very little" tar ((:commit . "8c46b1be9e45a38281aa9ddae79fda3c8e4cb5c5") (:authors ("chee" . "emacs@chee.party")) (:maintainers ("chee" . "emacs@chee.party")) (:maintainer "chee" . "emacs@chee.party") (:url . "https://opensource.chee.party/chee/feline-mode"))]) @@ -1945,7 +1946,7 @@ (foreign-regexp . [(20200325 50) nil "search and replace by foreign regexp." tar ((:commit . "e2dd47f2160cadc194eb156e7c76c3c869e6706e") (:authors ("K-talo Miyazaki <Keitaro dot Miyazaki at gmail dot com>")) (:maintainers ("K-talo Miyazaki <Keitaro dot Miyazaki at gmail dot com>")) (:maintainer "K-talo Miyazaki <Keitaro dot Miyazaki at gmail dot com>") (:keywords "convenience" "emulations" "matching" "tools" "unix" "wp"))]) (foreman-mode . [(20170725 1422) ((s (1 9 0)) (dash (2 10 0)) (dash-functional (1 2 0)) (f (0 17 2)) (emacs (24))) "View and manage Procfile-based applications" tar ((:commit . "22b3bb13134b617870ed1e888af739f4818be929") (:authors ("ZHOU Feng" . "zf.pascal@gmail.com")) (:maintainers ("ZHOU Feng" . "zf.pascal@gmail.com")) (:maintainer "ZHOU Feng" . "zf.pascal@gmail.com") (:keywords "foreman") (:url . "http://github.com/zweifisch/foreman-mode"))]) (forest-blue-theme . [(20160627 842) ((emacs (24))) "Emacs theme with a dark background." tar ((:commit . "58096ce1a25615d2bae806c3775bae3e2775019d") (:authors ("olkinn")) (:maintainers ("olkinn")) (:maintainer "olkinn"))]) - (forge . [(20240523 1103) ((emacs (26 3)) (compat (29 1 4 5)) (closql (20240405)) (dash (2 19 1)) (emacsql (20240124)) (ghub (20240507)) (let-alist (1 0 6)) (magit (20240428)) (markdown-mode (2 6)) (seq (2 24)) (transient (20240421)) (yaml (0 5 5))) "Access Git forges from Magit." tar ((:commit . "ceb6f5b8d257fe5e79532793cdd055c3dddd8806") (:authors ("Jonas Bernoulli" . "emacs.forge@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.forge@jonas.bernoulli.dev") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/forge"))]) + (forge . [(20240604 1645) ((emacs (26 3)) (compat (29 1 4 5)) (closql (20240405)) (dash (2 19 1)) (emacsql (20240124)) (ghub (20240507)) (let-alist (1 0 6)) (magit (20240428)) (markdown-mode (2 6)) (seq (2 24)) (transient (20240421)) (yaml (0 5 5))) "Access Git forges from Magit." tar ((:commit . "4727a5cc9a54223079ab6881772904e18585a342") (:authors ("Jonas Bernoulli" . "emacs.forge@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.forge@jonas.bernoulli.dev") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/forge"))]) (form-feed . [(20210508 1627) ((emacs (24 1))) "Display ^L glyphs as horizontal lines" tar ((:commit . "ac1f0ef30a11979f5dfe12d8c05a666739e486ff") (:authors ("Vasilij Schneidermann" . "mail@vasilij.de")) (:maintainers ("Vasilij Schneidermann" . "mail@vasilij.de")) (:maintainer "Vasilij Schneidermann" . "mail@vasilij.de") (:keywords "faces") (:url . "https://depp.brause.cc/form-feed"))]) (form-feed-st . [(20231002 2211) ((emacs (25 1))) "Display ^L glyphs as full-width horizontal lines" tar ((:commit . "f91c8daf35b7588e0aa24c8716c8cfd8ff0067c8") (:authors ("Leonardo Schripsema")) (:maintainers ("Leonardo Schripsema")) (:maintainer "Leonardo Schripsema") (:keywords "faces") (:url . "https://github.com/leodag/form-feed-st"))]) (format-all . [(20240511 1811) ((emacs (24 4)) (inheritenv (0 1)) (language-id (0 20))) "Auto-format C, C++, JS, Python, Ruby and 50 other languages" tar ((:commit . "c5ddfc5f3317eaa2a7541a818a0fce961e5e61dd") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainers ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "languages" "util") (:url . "https://github.com/lassik/emacs-format-all-the-code"))]) @@ -1988,7 +1989,7 @@ (function-args . [(20220516 1226) ((ivy (0 9 1))) "C++ completion for GNU Emacs" tar ((:commit . "beba049751fed78666c87bd146a6f1cf149bb819") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainers ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:url . "https://github.com/abo-abo/function-args"))]) (fuo . [(20190812 927) ((emacs (24 4))) "feeluown client." tar ((:commit . "0e4122f94a336a50c02bc96652d25ac3d74bedeb") (:authors ("cosven" . "yinshaowen241@gmail.com")) (:maintainers ("cosven" . "yinshaowen241@gmail.com")) (:maintainer "cosven" . "yinshaowen241@gmail.com") (:keywords "feeluown" "multimedia" "unix") (:url . "http://github.com/cosven/emacs-fuo"))]) (furl . [(20150509 316) nil "Friendly URL retrieval" tar ((:commit . "014438271e0ef27333dfcd599cb247f12a20d870") (:authors ("Natalie Weizenbaum" . "nweiz@google.com")) (:maintainers ("Natalie Weizenbaum" . "nweiz@google.com")) (:maintainer "Natalie Weizenbaum" . "nweiz@google.com"))]) - (fussy . [(20240224 1641) ((emacs (27 2)) (flx (0 5))) "Fuzzy completion style using `flx'" tar ((:commit . "0f58683355986e3f8d49734cb1f2ecdd71729439") (:authors ("James Nguyen" . "james@jojojames.com")) (:maintainers ("James Nguyen" . "james@jojojames.com")) (:maintainer "James Nguyen" . "james@jojojames.com") (:keywords "matching") (:url . "https://github.com/jojojames/fussy"))]) + (fussy . [(20240607 2153) ((emacs (27 2)) (flx (0 5))) "Fuzzy completion style using `flx'" tar ((:commit . "21f4ac6b971f61890d46308d7ac5db64c20228e6") (:authors ("James Nguyen" . "james@jojojames.com")) (:maintainers ("James Nguyen" . "james@jojojames.com")) (:maintainer "James Nguyen" . "james@jojojames.com") (:keywords "matching") (:url . "https://github.com/jojojames/fussy"))]) (futhark-mode . [(20240403 1143) ((emacs (24 3)) (cl-lib (0 5))) "major mode for editing Futhark source files" tar ((:commit . "98f9e7e890e082d45034f935d311a399326010ef") (:keywords "languages") (:url . "https://github.com/diku-dk/futhark-mode"))]) (fuz . [(20200104 524) ((emacs (25 1))) "Fast and precise fuzzy scoring/matching utils" tar ((:commit . "0b6b64cebde5675be3a28520ee16234db48d3b8b") (:authors ("Zhu Zihao" . "all_but_last@163.com")) (:maintainers ("Zhu Zihao" . "all_but_last@163.com")) (:maintainer "Zhu Zihao" . "all_but_last@163.com") (:keywords "lisp") (:url . "https://github.com/cireu/fuz.el"))]) (fuzzy . [(20240101 830) ((emacs (24 3))) "Fuzzy Matching" tar ((:commit . "295140da741ac02c1bd3dec69ccf7f6268d60ec5") (:authors ("Tomohiro Matsuyama" . "m2ym.pub@gmail.com")) (:maintainers ("Tomohiro Matsuyama" . "m2ym.pub@gmail.com")) (:maintainer "Tomohiro Matsuyama" . "m2ym.pub@gmail.com") (:keywords "convenience") (:url . "https://github.com/auto-complete/fuzzy-el"))]) @@ -2160,7 +2161,7 @@ (go-snippets . [(20180113 611) ((yasnippet (0 8 0))) "Yasnippets for go" tar ((:commit . "d437df148879566ffe7f2e503a3cf2602aa9fb28") (:keywords "snippets"))]) (go-stacktracer . [(20150430 2142) nil "parse Go stack traces" tar ((:commit . "a2ac6d801b389f80ca4e2fcc1ab44513a9e55976") (:authors ("Samer Masterson" . "samer@samertm.com")) (:maintainers ("Samer Masterson" . "samer@samertm.com")) (:maintainer "Samer Masterson" . "samer@samertm.com") (:keywords "tools") (:url . "https://github.com/samertm/go-stacktracer.el"))]) (go-tag . [(20230111 651) ((emacs (24 0)) (go-mode (1 5 0))) "Edit Golang struct field tag" tar ((:commit . "33f2059551d5298ca228d90f525b99d1a8d70364") (:authors ("Brantou" . "brantou89@gmail.com")) (:maintainers ("Brantou" . "brantou89@gmail.com")) (:maintainer "Brantou" . "brantou89@gmail.com") (:keywords "tools") (:url . "https://github.com/brantou/emacs-go-tag"))]) - (go-translate . [(20240531 208) ((emacs (28 1))) "Translation framework, configurable and scalable" tar ((:commit . "a857774be32bf2e7dabb308abbfde759a63a88fc") (:authors ("lorniu" . "lorniu@gmail.com")) (:maintainers ("lorniu" . "lorniu@gmail.com")) (:maintainer "lorniu" . "lorniu@gmail.com") (:keywords "convenience") (:url . "https://github.com/lorniu/go-translate"))]) + (go-translate . [(20240605 806) ((emacs (28 1))) "Translation framework, configurable and scalable" tar ((:commit . "64a01dc5cc1cbf1b79edbd970a94661f1a4dad90") (:authors ("lorniu" . "lorniu@gmail.com")) (:maintainers ("lorniu" . "lorniu@gmail.com")) (:maintainer "lorniu" . "lorniu@gmail.com") (:keywords "convenience") (:url . "https://github.com/lorniu/go-translate"))]) (gobgen . [(20161020 1523) ((emacs (24 4))) "Generate GObject descendants using a detailed form" tar ((:commit . "ed2c2b0d217deae293096f3cf14aa492791ddd4f") (:authors ("Gergely Polonkai" . "gergely@polonkai.eu")) (:maintainers ("Gergely Polonkai" . "gergely@polonkai.eu")) (:maintainer "Gergely Polonkai" . "gergely@polonkai.eu") (:keywords "gobject" "glib" "gtk" "helper" "utilities"))]) (god-mode . [(20221230 708) ((emacs (25 1))) "Minor mode for God-like command entering" tar ((:commit . "607aff10a7b27a8aa0c1a15c2c39337ab17cfda7") (:authors ("Chris Done" . "chrisdone@gmail.com")) (:maintainers ("Chris Done" . "chrisdone@gmail.com")) (:maintainer "Chris Done" . "chrisdone@gmail.com") (:url . "https://github.com/emacsorphanage/god-mode"))]) (godoctor . [(20180710 2152) nil "Frontend for godoctor" tar ((:commit . "4b45ff3d0572f0e84056e4c3ba91fcc178199859") (:authors ("Sangho Na" . "microamp@protonmail.com")) (:maintainers ("Sangho Na" . "microamp@protonmail.com")) (:maintainer "Sangho Na" . "microamp@protonmail.com") (:keywords "go" "golang" "refactoring") (:url . "https://github.com/microamp/godoctor.el"))]) @@ -2198,7 +2199,7 @@ (gpt-commit . [(20230716 331) ((emacs (27 1)) (magit (2 90)) (request (0 3 2))) "Commit messages with GPT in Emacs" tar ((:commit . "8a8883be2051eed499c5bc3035a75ff56d64d5ff") (:authors ("Youngwook Kim" . "youngwook.kim@gmail.com")) (:maintainers ("Youngwook Kim" . "youngwook.kim@gmail.com")) (:maintainer "Youngwook Kim" . "youngwook.kim@gmail.com") (:url . "https://github.com/ywkim/gpt-commit"))]) (gptai . [(20230530 1853) ((emacs (24 1))) "Integrate with the OpenAI API" tar ((:commit . "e7b8b91b425986868e8bc0edcac384ba47d4d4b7") (:authors ("Anton Hibl" . "antonhibl11@gmail.com")) (:maintainers ("Anton Hibl" . "antonhibl11@gmail.com")) (:maintainer "Anton Hibl" . "antonhibl11@gmail.com") (:keywords "comm" "convenience") (:url . "https://github.com/antonhibl/gptai"))]) (gptel . [(20240527 5) ((emacs (27 1)) (transient (0 4 0)) (compat (29 1 4 1))) "Interact with ChatGPT or other LLMs" tar ((:commit . "c0603cb973d160f3e450a0dec49dc5ae948f614c") (:authors ("Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com")) (:maintainers ("Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com")) (:maintainer "Karthik Chikmagalur" . "karthik.chikmagalur@gmail.com") (:keywords "convenience") (:url . "https://github.com/karthink/gptel"))]) - (gpx . [(20240525 1603) ((emacs (27 1))) "Major mode for GPX files" tar ((:commit . "b7cfc0f7ec53808f48c070f9c811934a7afcc580") (:authors ("Michał Krzywkowski" . "k.michal@zoho.com")) (:maintainers ("Michał Krzywkowski" . "k.michal@zoho.com")) (:maintainer "Michał Krzywkowski" . "k.michal@zoho.com") (:keywords "data" "tools") (:url . "https://github.com/mkcms/gpx-mode"))]) + (gpx . [(20240609 1337) ((emacs (27 1))) "Major mode for GPX files" tar ((:commit . "e5c6e6771d9c9a91757cf45306c3b2ab190d5a35") (:authors ("Michał Krzywkowski" . "k.michal@zoho.com")) (:maintainers ("Michał Krzywkowski" . "k.michal@zoho.com")) (:maintainer "Michał Krzywkowski" . "k.michal@zoho.com") (:keywords "data" "tools") (:url . "https://github.com/mkcms/gpx-mode"))]) (grab-mac-link . [(20210511 1303) ((emacs (24))) "Grab link from Mac Apps and insert it into Emacs" tar ((:commit . "5fdb03bf57bc4a530374b896e0f8b5139dc794e3") (:authors ("Xu Chunyang")) (:maintainers ("Xu Chunyang")) (:maintainer "Xu Chunyang") (:keywords "mac" "hyperlink") (:url . "https://github.com/xuchunyang/grab-mac-link.el"))]) (grab-x-link . [(20191113 848) ((emacs (24)) (cl-lib (0 5))) "Grab links from X11 apps and insert into Emacs" tar ((:commit . "d898db46e4864118359fdedfe915e180de3fe290") (:authors ("Xu Chunyang" . "mail@xuchunyang.me")) (:maintainers ("Xu Chunyang" . "mail@xuchunyang.me")) (:maintainer "Xu Chunyang" . "mail@xuchunyang.me") (:keywords "hyperlink") (:url . "https://github.com/xuchunyang/grab-x-link"))]) (gradle-mode . [(20150313 1905) ((s (1 8 0))) "Gradle integration with Emacs' compile" tar ((:commit . "579de06674551919cddac9cfe42129f4fb0155c9") (:authors ("Daniel Mijares" . "daniel.j.mijares@gmail.com")) (:maintainers ("Daniel Mijares" . "daniel.j.mijares@gmail.com")) (:maintainer "Daniel Mijares" . "daniel.j.mijares@gmail.com") (:keywords "gradle") (:url . "http://github.com/jacobono/emacs-gradle-mode"))]) @@ -2263,7 +2264,7 @@ (hamburger-menu . [(20220509 1341) ((emacs (28 1))) "Mode line hamburger menu" tar ((:commit . "06bc9d6872007a31226d7410d497a0acd98b272b") (:authors ("Iain Nicol")) (:maintainers ("Iain Nicol")) (:maintainer "Iain Nicol") (:keywords "hamburger" "menu") (:url . "https://gitlab.com/iain/hamburger-menu-mode"))]) (haml-mode . [(20230608 1833) ((emacs (24 1)) (cl-lib (0 5))) "Major mode for editing Haml files" tar ((:commit . "fe83c65c1f002f7c36480b758727c1afbad9a1b2") (:authors ("Natalie Weizenbaum")) (:maintainers ("Natalie Weizenbaum")) (:maintainer "Natalie Weizenbaum") (:keywords "markup" "languages" "html") (:url . "https://github.com/nex3/haml-mode"))]) (hamlet-mode . [(20131208 724) ((cl-lib (0 3)) (dash (2 3 0)) (s (1 7 0))) "Hamlet editing mode" tar ((:commit . "7362b955e556a3d007fa06945a27e5b99349527d") (:authors (nil . "Kata <lightquake@amateurtopologist.com")) (:maintainers (nil . "Kata <lightquake@amateurtopologist.com")) (:maintainer nil . "Kata <lightquake@amateurtopologist.com") (:keywords "wp" "languages" "comm") (:url . "https://github.com/lightquake/hamlet-mode"))]) - (hammy . [(20240423 407) ((emacs (28 1)) (svg-lib (0 2 5)) (ts (0 2 2))) "Programmable, interactive interval timers" tar ((:commit . "e3b2e365140abd87537edc09cd87fb04268bc439") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "convenience") (:url . "https://github.com/alphapapa/hammy.el"))]) + (hammy . [(20240608 359) ((emacs (28 1)) (svg-lib (0 2 5)) (ts (0 2 2))) "Programmable, interactive interval timers" tar ((:commit . "d5d154060bb13e9b61d74a83b25a12238973099d") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "convenience") (:url . "https://github.com/alphapapa/hammy.el"))]) (handle . [(20191029 856) ((emacs (25 1)) (parent-mode (2 3))) "A handle for major-mode generic functions." tar ((:commit . "e27b2d0b229923f81a2c8afa3e9c65ae9e84a0da") (:authors ("Uros Perisic")) (:maintainers ("Uros Perisic")) (:maintainer "Uros Perisic") (:keywords "convenience") (:url . "https://gitlab.com/jjzmajic/handle"))]) (handlebars-mode . [(20150211 1749) nil "A major mode for editing Handlebars files." tar ((:commit . "81f6b73fea8f397807781a1b51568397af21a6ef") (:authors ("Tony Gentilcore") ("Chris Wanstrath") ("Daniel Hackney") ("Daniel Evans")) (:maintainers ("Tony Gentilcore")) (:maintainer "Tony Gentilcore"))]) (handlebars-sgml-mode . [(20130623 2333) nil "Add Handlebars contextual indenting support to sgml-mode" tar ((:commit . "005282c33dfb6dbd2cfd46a4147d261504e8323c") (:authors ("Geoff Jacobsen" . "geoffjacobsen@gmail.com")) (:maintainers ("Geoff Jacobsen" . "geoffjacobsen@gmail.com")) (:maintainer "Geoff Jacobsen" . "geoffjacobsen@gmail.com") (:url . "http://github.com/jacott/handlebars-sgml-mode"))]) @@ -2291,7 +2292,7 @@ (headlong . [(20150417 1526) nil "reckless completion" tar ((:commit . "f6830f87f236eee88263cb6976125f72422abe72") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainers ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "completion") (:url . "https://github.com/abo-abo/headlong"))]) (heaven-and-hell . [(20190713 1830) ((emacs (24 4))) "easy toggle light/dark themes" tar ((:commit . "e1febfd60d060c110a1e43c5f093cd8537251308") (:authors ("Valentin Ignatev" . "valentignatev@gmail.com")) (:maintainers ("Valentin Ignatev" . "valentignatev@gmail.com")) (:maintainer "Valentin Ignatev" . "valentignatev@gmail.com") (:keywords "faces") (:url . "https://github.com/valignatev/heaven-and-hell"))]) (heex-ts-mode . [(20240113 1104) ((emacs (29 1))) "Major mode for Heex with tree-sitter support" tar ((:commit . "90142df2929956536dc1eaae3bb5ca04dc4232ab") (:authors ("Wilhelm H Kirschbaum")) (:maintainers ("Wilhelm H Kirschbaum")) (:maintainer "Wilhelm H Kirschbaum") (:keywords "heex" "languages" "tree-sitter") (:url . "https://github.com/wkirschbaum/elixir-ts-mode"))]) - (helm . [(20240530 1546) ((helm-core (3 9 9)) (wfnames (1 2))) "Helm is an Emacs incremental and narrowing framework" tar ((:commit . "aa51c1a8d6ff4e1e88e61e989f41ac3aa808a592") (:authors ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainers ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainer "Thierry Volpiatto" . "thievol@posteo.net") (:url . "https://emacs-helm.github.io/helm/"))]) + (helm . [(20240606 700) ((helm-core (3 9 9)) (wfnames (1 2))) "Helm is an Emacs incremental and narrowing framework" tar ((:commit . "a2f5185d61acc06d0c28baa586cd07b77ed03225") (:authors ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainers ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainer "Thierry Volpiatto" . "thievol@posteo.net") (:url . "https://emacs-helm.github.io/helm/"))]) (helm-R . [(20120820 14) ((helm (20120517)) (ess (20120509))) "helm-sources and some utilities for GNU R." tar ((:commit . "b0eb9d5f6a483a9dbe6eb6cf1f2024d4f5938bc2") (:authors ("myuhe <yuhei.maeda_at_gmail.com>")) (:maintainers ("myuhe")) (:maintainer "myuhe") (:keywords "convenience") (:url . "https://github.com/myuhe/helm-R.el"))]) (helm-ack . [(20141030 1226) ((helm (1 0)) (cl-lib (0 5))) "Ack command with helm interface" tar ((:commit . "5982f3cb6ec9f460ebbe06ec0ce7b3590bca3118") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainers ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-helm-ack"))]) (helm-ad . [(20151209 1015) ((dash (2 8 0)) (helm (1 6 2))) "helm source for Active Directory" tar ((:commit . "8ac044705d8620ee354a9cfa8cc1b865e83c0d55") (:authors ("Takahiro Noda" . "takahiro.noda+github@gmail.com")) (:maintainers ("Takahiro Noda" . "takahiro.noda+github@gmail.com")) (:maintainer "Takahiro Noda" . "takahiro.noda+github@gmail.com") (:keywords "comm"))]) @@ -2325,7 +2326,7 @@ (helm-comint . [(20231102 2029) ((emacs (25 1)) (helm (3 9 4))) "Comint prompt navigation for helm" tar ((:commit . "9215b2aa8f42f62cbda66a1503832abb7f491549") (:authors ("Pierre Neidhardt" . "mail@ambrevar.xyz")) (:maintainers ("Benedict Wang" . "foss@bhw.name")) (:maintainer "Benedict Wang" . "foss@bhw.name") (:keywords "processes" "matching") (:url . "https://github.com/benedicthw/helm-comint.git"))]) (helm-commandlinefu . [(20150611 545) ((emacs (24 1)) (helm (1 7 0)) (json (1 3)) (let-alist (1 0 3))) "Search and browse commandlinefu.com from helm" tar ((:commit . "9ee7e018c5db23ae9c8d1c8fa969876f15b7280d") (:authors ("Chunyang Xu" . "xuchunyang56@gmail.com")) (:maintainers ("Chunyang Xu" . "xuchunyang56@gmail.com")) (:maintainer "Chunyang Xu" . "xuchunyang56@gmail.com") (:keywords "commandlinefu.com") (:url . "https://github.com/xuchunyang/helm-commandlinefu"))]) (helm-company . [(20231113 701) ((helm (1 5 9)) (company (0 10 0))) "Helm interface for company-mode" tar ((:commit . "4622b82353220ee6cc33468f710fa5b6b253b7f1") (:authors ("Yasuyuki Oka" . "yasuyk@gmail.com")) (:maintainers ("Daniel Ralston" . "Sodel-the-Vociferous@users.noreply.github.com")) (:maintainer "Daniel Ralston" . "Sodel-the-Vociferous@users.noreply.github.com") (:url . "https://github.com/Sodel-the-Vociferous/helm-company"))]) - (helm-core . [(20240530 1546) ((emacs (25 1)) (async (1 9 8))) "Development files for Helm" tar ((:commit . "aa51c1a8d6ff4e1e88e61e989f41ac3aa808a592") (:authors ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainers ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainer "Thierry Volpiatto" . "thievol@posteo.net") (:url . "https://emacs-helm.github.io/helm/"))]) + (helm-core . [(20240608 1321) ((emacs (25 1)) (async (1 9 8))) "Development files for Helm" tar ((:commit . "56ac197f372a169101d84cf71c304221a06ceeb0") (:authors ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainers ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainer "Thierry Volpiatto" . "thievol@posteo.net") (:url . "https://emacs-helm.github.io/helm/"))]) (helm-cscope . [(20190615 41) ((xcscope (1 0)) (helm (1 6 7)) (cl-lib (0 5)) (emacs (24 1))) "Helm interface for xcscope.el." tar ((:commit . "af1d9e7f4460a88d7400b5a74d5da68084089ac1") (:authors ("alpha22jp" . "alpha22jp@gmail.com")) (:maintainers ("alpha22jp" . "alpha22jp@gmail.com")) (:maintainer "alpha22jp" . "alpha22jp@gmail.com") (:keywords "cscope" "helm") (:url . "https://github.com/alpha22jp/helm-cscope.el"))]) (helm-css-scss . [(20230522 1113) ((emacs (24 3)) (helm (1 0))) "CSS/SCSS/LESS Selectors with helm interface" tar ((:commit . "2169d83d8fdc661241df208cb3235112735d936e") (:authors ("Shingo Fukuyama - http://fukuyama.co")) (:maintainers ("Shingo Fukuyama - http://fukuyama.co")) (:maintainer "Shingo Fukuyama - http://fukuyama.co") (:keywords "convenience" "scss" "css" "less" "selector" "helm") (:url . "https://github.com/ShingoFukuyama/helm-css-scss"))]) (helm-ctest . [(20220721 400) ((s (1 9 0)) (dash (2 11 0)) (helm-core (3 6 0))) "Run ctest from within emacs" tar ((:commit . "48edc9fa862219da34feb423c06c33d8f6d43722") (:authors ("Dan LaManna" . "me@danlamanna.com")) (:maintainers ("Dan LaManna" . "me@danlamanna.com")) (:maintainer "Dan LaManna" . "me@danlamanna.com") (:keywords "helm" "ctest"))]) @@ -2473,7 +2474,7 @@ (helm-zhihu-daily . [(20160625 1145) ((helm (1 0)) (cl-lib (0 5)) (emacs (24 4))) "Helm interface for 知乎日报 (http://daily.zhihu.com)" tar ((:commit . "be27dcc6be1eb97663b65581a9a5c0fc81cfaba7") (:authors ("Chunyang Xu" . "xuchunyang56@gmail.com")) (:maintainers ("Chunyang Xu" . "xuchunyang56@gmail.com")) (:maintainer "Chunyang Xu" . "xuchunyang56@gmail.com") (:url . "https://github.com/xuchunyang/helm-zhihu-daily"))]) (help-find . [(20220929 822) ((emacs (25 2)) (dash (2 12))) "Additional help functions for working with keymaps" tar ((:commit . "ef7266fc480367c12bff64817c875af940d0c9c0") (:authors ("Duncan Burke" . "duncankburke@gmail.com")) (:maintainers ("Duncan Burke" . "duncankburke@gmail.com")) (:maintainer "Duncan Burke" . "duncankburke@gmail.com") (:keywords "help") (:url . "https://github.com/duncanburke/help-find"))]) (help-find-org-mode . [(20181204 234) ((emacs (24 4))) "Advise help to find org source over tangled code" tar ((:commit . "c6fa2c8a8e9381572190010a9fa01f2be78f2790") (:authors ("Eric Crosson" . "eric.s.crosson@utexas.com")) (:maintainers ("Eric Crosson" . "eric.s.crosson@utexas.com")) (:maintainer "Eric Crosson" . "eric.s.crosson@utexas.com") (:keywords "convenience") (:url . "https://github.com/EricCrosson/help-find-org-mode"))]) - (helpful . [(20231028 516) ((emacs (25)) (dash (2 18 0)) (s (1 11 0)) (f (0 20 0)) (elisp-refs (1 2))) "A better *help* buffer" tar ((:commit . "a32a5b3d959a7fccf09a71d97b3d7c888ac31c69") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainers ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk") (:keywords "help" "lisp") (:url . "https://github.com/Wilfred/helpful"))]) + (helpful . [(20240607 1518) ((emacs (25)) (dash (2 18 0)) (s (1 11 0)) (f (0 20 0)) (elisp-refs (1 2))) "A better *help* buffer" tar ((:commit . "a835b7ec59004a13f4a09ff127a785a92b957e8f") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainers ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk") (:keywords "help" "lisp") (:url . "https://github.com/Wilfred/helpful"))]) (hemera-theme . [(20180916 924) ((emacs (24))) "Light theme" tar ((:commit . "b67c902b210b37b00cac68726822404543147ba8") (:authors ("Guido Schmidt")) (:maintainers ("Guido Schmidt" . "guido.schmidt.2912@gmail.com")) (:maintainer "Guido Schmidt" . "guido.schmidt.2912@gmail.com") (:keywords "themes" "light-theme") (:url . "https://github.com/GuidoSchmidt/emacs-hemera-theme"))]) (hemisu-theme . [(20130508 1844) nil "Hemisu for Emacs." tar ((:commit . "ae593ac58e6bffef97467259c1d1472840385e84") (:authors ("Andrzej Sliwa")) (:maintainers ("Andrzej Sliwa")) (:maintainer "Andrzej Sliwa") (:url . "http://github/anrzejsliwa/django-theme"))]) (hercules . [(20200420 747) ((emacs (24 4)) (which-key (3 3 2))) "An auto-magical, which-key-based hydra banisher." tar ((:commit . "557da39878d0637395fdded91243b340c37eff7b") (:authors ("Uros Perisic")) (:maintainers ("Uros Perisic")) (:maintainer "Uros Perisic") (:keywords "convenience") (:url . "https://gitlab.com/jjzmajic/hercules"))]) @@ -2576,8 +2577,8 @@ (hydandata-light-theme . [(20190809 1925) nil "A light color theme that is easy on your eyes" tar ((:commit . "812ffa4bee3163098ef66ee4506feed45018be4e") (:authors ("David Chkhikvadze" . "david@chkhd.net")) (:maintainers ("David Chkhikvadze" . "david@chkhd.net")) (:maintainer "David Chkhikvadze" . "david@chkhd.net") (:keywords "color-theme" "theme") (:url . "https://github.com/chkhd/hydandata-light-theme"))]) (hyde . [(20160508 308) nil "Major mode to help create and manage Jekyll blogs" tar ((:commit . "a8cd6ed00ecd8d7de0ded2f4867015b412b15b76"))]) (hydra . [(20220910 1206) ((cl-lib (0 5)) (lv (0))) "Make bindings that stick around." tar ((:commit . "317e1de33086637579a7aeb60f77ed0405bf359b") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainers ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "bindings") (:url . "https://github.com/abo-abo/hydra"))]) - (hyperbole . [(20240602 811) ((emacs (27 1))) "GNU Hyperbole: The Everyday Hypertextual Information Manager" tar ((:commit . "c3050387ea625842e2fa93fa9d7f44bd8d0c1e35") (:authors ("Robert Weiner" . "rsw@gnu.org")) (:maintainers ("Mats Lidell" . "matsl@gnu.org")) (:maintainer "Mats Lidell" . "matsl@gnu.org") (:keywords "comm" "convenience" "files" "frames" "hypermedia" "languages" "mail" "matching" "mouse" "multimedia" "outlines" "tools" "wp") (:url . "http://www.gnu.org/software/hyperbole"))]) - (hyperdrive . [(20240531 2312) ((emacs (28 1)) (map (3 0)) (compat (29 1 4 4)) (plz (0 7 2)) (persist (0 6)) (taxy-magit-section (0 13)) (transient (0 6 0))) "P2P filesystem" tar ((:commit . "bb7a699a4bc44ffa448ceddfd4cb89a0c6bc93aa") (:authors ("Joseph Turner" . "joseph@ushin.org")) (:maintainers ("Joseph Turner" . "~ushin/ushin@lists.sr.ht")) (:maintainer "Joseph Turner" . "~ushin/ushin@lists.sr.ht") (:url . "https://git.sr.ht/~ushin/hyperdrive.el"))]) + (hyperbole . [(20240604 1153) ((emacs (27 1))) "GNU Hyperbole: The Everyday Hypertextual Information Manager" tar ((:commit . "fbb6f81bcd2627ecd6158f13644d00010fb16f42") (:authors ("Robert Weiner" . "rsw@gnu.org")) (:maintainers ("Mats Lidell" . "matsl@gnu.org")) (:maintainer "Mats Lidell" . "matsl@gnu.org") (:keywords "comm" "convenience" "files" "frames" "hypermedia" "languages" "mail" "matching" "mouse" "multimedia" "outlines" "tools" "wp") (:url . "http://www.gnu.org/software/hyperbole"))]) + (hyperdrive . [(20240609 550) ((emacs (28 1)) (map (3 0)) (compat (29 1 4 4)) (org (9 7 3)) (plz (0 7 2)) (persist (0 6)) (taxy-magit-section (0 13)) (transient (0 6 0))) "P2P filesystem" tar ((:commit . "59a70f9127b77fe3997652af58779dbd97af8193") (:authors ("Joseph Turner" . "joseph@ushin.org")) (:maintainers ("Joseph Turner" . "~ushin/ushin@lists.sr.ht")) (:maintainer "Joseph Turner" . "~ushin/ushin@lists.sr.ht") (:url . "https://git.sr.ht/~ushin/hyperdrive.el"))]) (hyperkitty . [(20220226 1951) ((request (0 3 2)) (emacs (25 1))) "Emacs interface for Hyperkitty archives" tar ((:commit . "2c1d22ff017d096c359aa151e6a29f7214a58118") (:authors ("Abhilash Raj" . "maxking@asynchronous.in")) (:maintainers ("Abhilash Raj" . "maxking@asynchronous.in")) (:maintainer "Abhilash Raj" . "maxking@asynchronous.in") (:keywords "mail" "hyperkitty" "mailman") (:url . "https://github.com/maxking/hyperkitty.el"))]) (hyperlist-mode . [(20230119 28) ((emacs (24))) "A major-mode for viewing Hyperlists" tar ((:commit . "480dbf33ca72e7b5fade952aaf0d5a5eb43acb1d") (:authors ("Wojciech Siewierski")) (:maintainers ("Wojciech Siewierski")) (:maintainer "Wojciech Siewierski") (:keywords "outlines") (:url . "https://github.com/vifon/hyperlist-mode"))]) (hyperspace . [(20230518 442) ((emacs (25)) (s (1 12 0))) "Get there from here" tar ((:commit . "f574d07fd8715e806ba4f0487b73c699963baed3") (:authors ("Ian Eure" . "ian@retrospec.tv")) (:maintainers ("Ian Eure" . "ian@retrospec.tv")) (:maintainer "Ian Eure" . "ian@retrospec.tv") (:keywords "tools" "convenience") (:url . "https://github.com/ieure/hyperspace-el"))]) @@ -2631,7 +2632,7 @@ (iedit . [(20220216 717) nil "Edit multiple regions in the same way simultaneously." tar ((:commit . "dd5d75b38ee0c52ad81245a8e5c932d3f5c4772d") (:authors ("Victor Ren" . "victorhge@gmail.com")) (:maintainers ("Victor Ren" . "victorhge@gmail.com")) (:maintainer "Victor Ren" . "victorhge@gmail.com") (:keywords "occurrence" "region" "simultaneous" "refactoring") (:url . "https://github.com/victorhge/iedit"))]) (ietf-docs . [(20190420 851) nil "Fetch, Cache and Load IETF documents" tar ((:commit . "ae157549eae5ec78dcbf215c2f48cb662b73abd0") (:authors ("Christian E. Hopps" . "chopps@gmail.com")) (:maintainers ("Christian E. Hopps" . "chopps@gmail.com")) (:maintainer "Christian E. Hopps" . "chopps@gmail.com") (:keywords "ietf" "rfc") (:url . "https://github.com/choppsv1/ietf-docs"))]) (iflipb . [(20220612 858) nil "Interactively flip between recently visited buffers" tar ((:commit . "9ec1888335107bd314e8f40b3e113d525fed8083") (:authors ("Joel Rosdahl" . "joel@rosdahl.net")) (:maintainers ("Joel Rosdahl" . "joel@rosdahl.net")) (:maintainer "Joel Rosdahl" . "joel@rosdahl.net") (:url . "https://github.com/jrosdahl/iflipb"))]) - (igist . [(20240520 1402) ((emacs (27 1)) (ghub (3 6 0)) (transient (0 4 1))) "List, create, update and delete GitHub gists" tar ((:commit . "126e9924f698b97c932f7edb1628aad44e8445b3") (:authors ("Karim Aziiev" . "karim.aziiev@gmail.com")) (:maintainers ("Karim Aziiev" . "karim.aziiev@gmail.com")) (:maintainer "Karim Aziiev" . "karim.aziiev@gmail.com") (:keywords "tools") (:url . "https://github.com/KarimAziev/igist"))]) + (igist . [(20240608 1258) ((emacs (27 1)) (ghub (3 6 0)) (transient (0 4 1))) "List, create, update and delete GitHub gists" tar ((:commit . "edb67a6a983bb83e2543ae26f3f0e7a346f7e215") (:authors ("Karim Aziiev" . "karim.aziiev@gmail.com")) (:maintainers ("Karim Aziiev" . "karim.aziiev@gmail.com")) (:maintainer "Karim Aziiev" . "karim.aziiev@gmail.com") (:keywords "tools") (:url . "https://github.com/KarimAziev/igist"))]) (ignoramus . [(20220611 1514) ((emacs (24 3))) "Ignore backups, build files, et al." tar ((:commit . "f5e4a66191be12c2fc3cf42a5e0849fcc8518a3f") (:authors ("Roland Walker" . "walker@pobox.com")) (:maintainers ("Roland Walker" . "walker@pobox.com")) (:maintainer "Roland Walker" . "walker@pobox.com") (:keywords "convenience" "tools") (:url . "http://github.com/rolandwalker/ignoramus"))]) (igv . [(20141210 1227) nil "Control Integrative Genomic Viewer within Emacs" tar ((:commit . "47ac6ceede252f451348a2c696398c0cb5279555") (:authors ("Stefano Barbi" . "stefanobarbi@gmail.com")) (:maintainers ("Stefano Barbi" . "stefanobarbi@gmail.com")) (:maintainer "Stefano Barbi" . "stefanobarbi@gmail.com"))]) (image+ . [(20150707 1616) ((cl-lib (0 3))) "Image manipulate extensions for Emacs" tar ((:commit . "6834d0c09bb4df9ecc0d7a559bd7827fed48fffc") (:authors ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainers ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainer "Masahiro Hayashi" . "mhayashi1120@gmail.com") (:keywords "multimedia" "extensions") (:url . "https://github.com/mhayashi1120/Emacs-imagex"))]) @@ -2669,7 +2670,7 @@ (inf-crystal . [(20180119 211) ((emacs (24 3)) (crystal-mode (0 1 0))) "Run a Inferior-Crystal process in a buffer" tar ((:commit . "dd5c85e621976ea09b602182a15396e3b510ec63") (:authors ("Brantou" . "brantou89@gmail.com")) (:maintainers ("Brantou" . "brantou89@gmail.com")) (:maintainer "Brantou" . "brantou89@gmail.com") (:keywords "languages" "crystal") (:url . "https://github.com/brantou/inf-crystal.el"))]) (inf-elixir . [(20230611 1945) ((emacs (25 1))) "Run an interactive Elixir shell" tar ((:commit . "77ac6af83eb4b816c62f58a0298b1bae0c3d69fd") (:authors ("Jonathan Arnett" . "jonathan.arnett@protonmail.com")) (:maintainers ("Jonathan Arnett" . "jonathan.arnett@protonmail.com")) (:maintainer "Jonathan Arnett" . "jonathan.arnett@protonmail.com") (:keywords "languages" "processes" "tools") (:url . "https://github.com/J3RN/inf-elixir"))]) (inf-mongo . [(20180408 1338) nil "Run a MongoDB shell process in a buffer" tar ((:commit . "2e498d1c88bd1904eeec18ed06b1a0cf8bdc2a92") (:authors ("Tobias Svensson")) (:maintainers ("Tobias Svensson")) (:maintainer "Tobias Svensson") (:keywords "databases" "mongodb") (:url . "http://github.com/endofunky/inf-mongo"))]) - (inf-ruby . [(20240509 143) ((emacs (26 1))) "Run a Ruby process in a buffer" tar ((:commit . "1dcaa0aad2eec23582263f934005140ddf70f52c") (:authors ("Yukihiro Matsumoto") ("Nobuyoshi Nakada") ("Cornelius Mika" . "cornelius.mika@gmail.com") ("Dmitry Gutov" . "dgutov@yandex.ru") ("Kyle Hargraves" . "pd@krh.me")) (:maintainers ("Dmitry Gutov" . "dmitry@gutov.dev")) (:maintainer "Dmitry Gutov" . "dmitry@gutov.dev") (:keywords "languages" "ruby") (:url . "http://github.com/nonsequitur/inf-ruby"))]) + (inf-ruby . [(20240605 255) ((emacs (26 1))) "Run a Ruby process in a buffer" tar ((:commit . "0cfe8b2fb1ab222ed423a8e6f339d398fa32966f") (:authors ("Yukihiro Matsumoto") ("Nobuyoshi Nakada") ("Cornelius Mika" . "cornelius.mika@gmail.com") ("Dmitry Gutov" . "dgutov@yandex.ru") ("Kyle Hargraves" . "pd@krh.me")) (:maintainers ("Dmitry Gutov" . "dmitry@gutov.dev")) (:maintainer "Dmitry Gutov" . "dmitry@gutov.dev") (:keywords "languages" "ruby") (:url . "http://github.com/nonsequitur/inf-ruby"))]) (inferior-islisp . [(20220924 1040) ((emacs (26 3)) (islisp-mode (0 2))) "Run inferior ISLisp processes" tar ((:commit . "423b84fe4cc6944e36971225b3e19c888e7e4690") (:authors ("Fermin Munoz")) (:maintainers ("Fermin Munoz" . "fmfs@posteo.net")) (:maintainer "Fermin Munoz" . "fmfs@posteo.net") (:keywords "islisp" "lisp" "programming") (:url . "https://gitlab.com/sasanidas/islisp-mode"))]) (inflections . [(20210110 2237) ((cl-lib (0 5)) (emacs (24))) "convert english words between singular and plural" tar ((:commit . "55caa66a7cc6e0b1a76143fd40eff38416928941") (:authors ("Dmitry Galinsky, Howard Yeh")) (:maintainers ("Dmitry Galinsky, Howard Yeh")) (:maintainer "Dmitry Galinsky, Howard Yeh") (:keywords "languages" "tools" "wp") (:url . "https://github.com/eschulte/jump.el"))]) (info-beamer . [(20210427 1033) ((emacs (24 4))) "Utilities for working with info-beamer" tar ((:commit . "6b4cc29f1aec72d8e23b2c25a99cdd84e6cdc92b") (:authors ("Daniel Kraus" . "daniel@kraus.my")) (:maintainers ("Daniel Kraus" . "daniel@kraus.my")) (:maintainer "Daniel Kraus" . "daniel@kraus.my") (:keywords "tools" "processes" "comm") (:url . "https://github.com/dakra/info-beamer.el"))]) @@ -2773,7 +2774,7 @@ (ivy-ycmd . [(20180909 1225) ((ycmd (1 3)) (emacs (24)) (ivy (0 10 0)) (dash (2 14 1))) "Ivy interface to ycmd" tar ((:commit . "25bfee8f676e4ecbb645e4f30b47083410a00c58") (:authors ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainers ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainer "Austin Bingham" . "austin.bingham@gmail.com") (:keywords "tools") (:url . "https://github.com/abingham/emacs-ivy-ycmd"))]) (ivy-youtube . [(20230503 1509) ((request (0 2 0)) (ivy (0 8 0)) (cl-lib (0 5))) "Query YouTube and play videos in your browser" tar ((:commit . "e7a7cc860e967500857e5fd85d8e397c6d752ee1") (:authors ("Brunno dos Santos")) (:maintainers ("Brunno dos Santos")) (:maintainer "Brunno dos Santos") (:keywords "youtube" "multimedia" "mpv" "vlc") (:url . "https://github.com/squiter/ivy-youtube"))]) (ix . [(20131027 1657) ((grapnel (0 5 3))) "Emacs client for http://ix.io pastebin" tar ((:commit . "498dac674f4f1910d39087b1457c5da5465a0614") (:authors ("Abhishek L" . "abhishekl.2006@gmail.com")) (:maintainers ("Abhishek L" . "abhishekl.2006@gmail.com")) (:maintainer "Abhishek L" . "abhishekl.2006@gmail.com") (:url . "http://www.github.com/theanalyst/ix.el"))]) - (j-mode . [(20171224 1856) nil "Major mode for editing J programs" tar ((:commit . "e8725ac8af95498faabb2ca3ab3bd809a8f148e6") (:keywords "j" "languages") (:url . "http://github.com/zellio/j-mode"))]) + (j-mode . [(20240609 1501) nil "Major mode for editing J programs" tar ((:commit . "690c7acdfccac49a54798cc317aee1cd247fc2a2") (:keywords "j" "languages") (:url . "http://github.com/zellio/j-mode"))]) (jabber . [(20240525 206) ((emacs (27 1)) (fsm (0 2)) (srv (0 2))) "A Jabber client for Emacs." tar ((:commit . "306e96589263c266b9d3738b33cf55be1a91240a") (:authors ("Magnus Henoch" . "mange@freemail.hu")) (:maintainer "wgreenhouse" . "wgreenhouse@tilde.club") (:keywords "comm") (:url . "https://codeberg.org/emacs-jabber/emacs-jabber"))]) (jack . [(20221122 632) ((emacs (28 1))) "HTML generator library" tar ((:commit . "3b4ea97fcc107d0ffd201ea695129af52f390113") (:authors ("Tony Aldon" . "tony.aldon.adm@gmail.com")) (:maintainers ("Tony Aldon" . "tony.aldon.adm@gmail.com")) (:maintainer "Tony Aldon" . "tony.aldon.adm@gmail.com") (:keywords "lisp" "html") (:url . "https://github.com/tonyaldon/jack"))]) (jack-connect . [(20220201 1417) nil "Manage jack connections within Emacs" tar ((:commit . "1acaebfe8f37f0194e95c3e812c9515a6f688eee") (:authors ("Stefano Barbi" . "stefanobarbi@gmail.com")) (:maintainers ("Stefano Barbi" . "stefanobarbi@gmail.com")) (:maintainer "Stefano Barbi" . "stefanobarbi@gmail.com"))]) @@ -2812,7 +2813,7 @@ (jetbrains-darcula-theme . [(20230223 1901) nil "A complete port of the default JetBrains Darcula theme" tar ((:commit . "46f153385e50998826ca13e18056c6a972768cfd") (:authors ("Ian Y.E. Pan")) (:maintainers ("Ian Y.E. Pan")) (:maintainer "Ian Y.E. Pan") (:url . "https://github.com/ianpan870102/jetbrains-darcula-emacs-theme"))]) (jg-quicknav . [(20170809 130) ((s (1 9 0)) (cl-lib (0 5))) "Quickly navigate the file system to find a file." tar ((:commit . "c8d53e774d63e68a944092c08a026b57da741038") (:authors ("Jeff Gran" . "jeff@jeffgran.com")) (:maintainers ("Jeff Gran" . "jeff@jeffgran.com")) (:maintainer "Jeff Gran" . "jeff@jeffgran.com") (:keywords "navigation") (:url . "https://github.com/jeffgran/jg-quicknav"))]) (jinja2-mode . [(20220117 807) nil "A major mode for jinja2" tar ((:commit . "03e5430a7efe1d163a16beaf3c82c5fd2c2caee1") (:authors ("Florian Mounier aka paradoxxxzero")) (:maintainers ("Florian Mounier aka paradoxxxzero")) (:maintainer "Florian Mounier aka paradoxxxzero"))]) - (jinx . [(20240515 1016) ((emacs (27 1)) (compat (29 1 4 4))) "Enchanted Spell Checker" tar ((:commit . "3c36f1eb31713869ffbdbf55971671efa4f01966") (:authors ("Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainers ("Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainer "Daniel Mendler" . "mail@daniel-mendler.de") (:keywords "convenience" "text") (:url . "https://github.com/minad/jinx"))]) + (jinx . [(20240606 909) ((emacs (27 1)) (compat (29 1 4 4))) "Enchanted Spell Checker" tar ((:commit . "81d0a092b3ca4c5036af45c9411ebbe66eb123ea") (:authors ("Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainers ("Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainer "Daniel Mendler" . "mail@daniel-mendler.de") (:keywords "convenience" "text") (:url . "https://github.com/minad/jinx"))]) (jira-markup-mode . [(20150601 2109) nil "Emacs Major mode for JIRA-markup-formatted text files" tar ((:commit . "53bf083fdbece483f1351f32085b424b38c4c1f2") (:authors ("Matthias Nuessler" . "m.nuessler@web.de>")) (:maintainers ("Matthias Nuessler" . "m.nuessler@web.de>")) (:maintainer "Matthias Nuessler" . "m.nuessler@web.de>") (:keywords "jira" "markup") (:url . "https://github.com/mnuessler/jira-markup-mode"))]) (jiralib2 . [(20200520 2031) ((emacs (25)) (request (0 3)) (dash (2 14 1))) "JIRA REST API bindings to Elisp" tar ((:commit . "c21c4e759eff549dbda11099f2f680b78d7f5a01") (:authors ("Henrik Nyman" . "h@nyymanni.com")) (:maintainers ("Henrik Nyman" . "h@nyymanni.com")) (:maintainer "Henrik Nyman" . "h@nyymanni.com") (:keywords "comm" "jira" "rest" "api") (:url . "https://github.com/nyyManni/jiralib2"))]) (jirascope . [(20240122 2130) ((emacs (25 1))) "A Jira client" tar ((:commit . "61acd8d6adbd6b25ebcc5436b4dce6d5c6d2981c") (:authors ("Stanisław Zagórowski" . "duckonaut@gmail.com")) (:maintainers ("Stanisław Zagórowski" . "duckonaut@gmail.com")) (:maintainer "Stanisław Zagórowski" . "duckonaut@gmail.com") (:keywords "tools") (:url . "https://github.com/Duckonaut/jirascope"))]) @@ -2847,7 +2848,7 @@ (jsfmt . [(20180920 1008) nil "Interface to jsfmt command for javascript files" tar ((:commit . "ca141a135c7700eaedef92561d334e1fb7dc28a1") (:authors ("Brett Langdon" . "brett@blangdon.com")) (:maintainers ("Brett Langdon" . "brett@blangdon.com")) (:maintainer "Brett Langdon" . "brett@blangdon.com") (:url . "https://github.com/brettlangdon/jsfmt.el"))]) (json-mode . [(20240427 1245) ((json-snatcher (1 0 0)) (emacs (24 4))) "Major mode for editing JSON files" tar ((:commit . "77125b01c0ddce537085201098bea9b4b8ba6be3") (:authors ("Josh Johnston") ("taku0")) (:maintainers ("Josh Johnston")) (:maintainer "Josh Johnston") (:url . "https://github.com/joshwnj/json-mode"))]) (json-navigator . [(20230904 1757) ((emacs (25 1)) (hierarchy (0 6 0))) "View and navigate JSON structures" tar ((:commit . "f3489153e8509f88296786cb00e31f59597a43f2") (:authors ("Damien Cassou" . "damien@cassou.me")) (:maintainers ("Damien Cassou" . "damien@cassou.me")) (:maintainer "Damien Cassou" . "damien@cassou.me") (:url . "https://github.com/DamienCassou/json-navigator"))]) - (json-par . [(20240601 1234) ((emacs (24 4)) (json-mode (1 7 0))) "Minor mode for structural editing of JSON" tar ((:commit . "3b057977748abc6fc0cba221ede903096d7c7248") (:authors ("taku0" . "mxxouy6x3m_github@tatapa.org")) (:maintainers ("taku0" . "mxxouy6x3m_github@tatapa.org")) (:maintainer "taku0" . "mxxouy6x3m_github@tatapa.org") (:keywords "abbrev" "convenience" "files") (:url . "https://github.com/taku0/json-par"))]) + (json-par . [(20240608 725) ((emacs (24 4)) (json-mode (1 7 0))) "Minor mode for structural editing of JSON" tar ((:commit . "c4a9566142de6b0812cf4dfe0b0bf49b3e35f038") (:authors ("taku0" . "mxxouy6x3m_github@tatapa.org")) (:maintainers ("taku0" . "mxxouy6x3m_github@tatapa.org")) (:maintainer "taku0" . "mxxouy6x3m_github@tatapa.org") (:keywords "abbrev" "convenience" "files") (:url . "https://github.com/taku0/json-par"))]) (json-process-client . [(20230903 1305) ((emacs (27 1))) "Interact with a TCP process using JSON" tar ((:commit . "c4385859ada9b7803698a1f0199fea7fc8880214") (:authors ("Nicolas Petton" . "nicolas@petton.fr") ("Damien Cassou" . "damien@cassou.me")) (:maintainers ("Nicolas Petton" . "nicolas@petton.fr")) (:maintainer "Nicolas Petton" . "nicolas@petton.fr") (:url . "https://gitlab.petton.fr/nico/json-process-client"))]) (json-reformat . [(20220905 2342) ((emacs (24 3))) "Reformatting tool for JSON" tar ((:commit . "e9999b1f1fc933c02ff44f4136602b6a45ed59c6") (:authors ("Wataru MIYAGUNI" . "gonngo@gmail.com")) (:maintainers ("Wataru MIYAGUNI" . "gonngo@gmail.com")) (:maintainer "Wataru MIYAGUNI" . "gonngo@gmail.com") (:keywords "json") (:url . "https://github.com/gongo/json-reformat"))]) (json-rpc . [(20200417 1629) ((emacs (24 1)) (cl-lib (0 5))) "JSON-RPC library" tar ((:commit . "81a5a520072e20d18aeab2aac4d66c046b031e56") (:authors ("Christopher Wellons" . "wellons@nullprogram.com")) (:maintainers ("Christopher Wellons" . "wellons@nullprogram.com")) (:maintainer "Christopher Wellons" . "wellons@nullprogram.com") (:url . "https://github.com/skeeto/elisp-json-rpc"))]) @@ -2859,7 +2860,7 @@ (jss . [(20130508 1423) ((emacs (24 1)) (websocket (0)) (js2-mode (0))) "An emacs interface to webkit and mozilla debuggers" tar ((:commit . "41749257aecf13c7bd6ed489b5ab3304d06e40bc") (:authors ("Marco Baringer" . "mb@bese.it")) (:maintainers ("Marco Baringer" . "mb@bese.it")) (:maintainer "Marco Baringer" . "mb@bese.it") (:keywords "languages"))]) (jst . [(20150604 1138) ((s (1 9)) (f (0 17)) (dash (2 10)) (pcache (0 3)) (emacs (24 4))) "JS test mode" tar ((:commit . "865ff97449a4cbbcb40d38b4908cf4d7b22a5108") (:authors ("Cheung Hoi Yu" . "yeannylam@gmail.com")) (:maintainers ("Cheung Hoi Yu" . "yeannylam@gmail.com")) (:maintainer "Cheung Hoi Yu" . "yeannylam@gmail.com") (:keywords "js" "javascript" "jasmine" "coffee" "coffeescript") (:url . "https://github.com/cheunghy/jst-mode"))]) (jtags . [(20160211 2029) nil "enhanced tags functionality for Java development" tar ((:commit . "f7d29e1635ef7ee4ee2cdb8f1f6ab83e1015c84a") (:authors ("Alexander Baltatzis" . "alexander@baltatzis.com") ("Johan Dykstrom" . "jody4711-sf@yahoo.se")) (:maintainers ("Johan Dykstrom" . "jody4711-sf@yahoo.se")) (:maintainer "Johan Dykstrom" . "jody4711-sf@yahoo.se") (:keywords "languages" "tools") (:url . "http://jtags.sourceforge.net"))]) - (jtsx . [(20240519 1546) ((emacs (29 1))) "Extends JSX/TSX built-in support" tar ((:commit . "7bbf02f046e375c23fe5a90eb0a9557e843eba41") (:authors ("Loïc Lemaître" . "loic.lemaitre@gmail.com")) (:maintainers ("Loïc Lemaître" . "loic.lemaitre@gmail.com")) (:maintainer "Loïc Lemaître" . "loic.lemaitre@gmail.com") (:keywords "languages") (:url . "https://github.com/llemaitre19/jtsx"))]) + (jtsx . [(20240603 1501) ((emacs (29 1))) "Extends JSX/TSX built-in support" tar ((:commit . "2ad20cef1b2a6fc1e8b282f34127de82f6e463b1") (:authors ("Loïc Lemaître" . "loic.lemaitre@gmail.com")) (:maintainers ("Loïc Lemaître" . "loic.lemaitre@gmail.com")) (:maintainer "Loïc Lemaître" . "loic.lemaitre@gmail.com") (:keywords "languages") (:url . "https://github.com/llemaitre19/jtsx"))]) (julia-formatter . [(20231130 1512) ((emacs (27 1)) (session-async (0 0 5))) "Use JuliaFormatter.jl for julia code" tar ((:commit . "4b40481cc9c0dcb3c9704436e00d613067d44bf5") (:authors ("Felipe Lema" . "felipe.lema@mortemale.org")) (:maintainers ("Felipe Lema" . "felipe.lema@mortemale.org")) (:maintainer "Felipe Lema" . "felipe.lema@mortemale.org") (:keywords "convenience" "tools") (:url . "https://codeberg.org/FelipeLema/julia-formatter.el"))]) (julia-mode . [(20240506 1205) ((emacs (26 1))) "Major mode for editing Julia source code" tar ((:commit . "d360ad5285b8a0be1818fd6c2b4307c34e468c6e") (:keywords "languages") (:url . "https://github.com/JuliaEditorSupport/julia-emacs"))]) (julia-repl . [(20240408 850) ((emacs (25 1)) (s (1 12))) "A minor mode for a Julia REPL" tar ((:commit . "801d0fc3d8f6f08f66a11515e6517739a0b312a1") (:authors ("Tamas Papp" . "tkpapp@gmail.com")) (:maintainers ("Tamas Papp" . "tkpapp@gmail.com")) (:maintainer "Tamas Papp" . "tkpapp@gmail.com") (:keywords "languages") (:url . "https://github.com/tpapp/julia-repl"))]) @@ -2873,7 +2874,7 @@ (jump-to-line . [(20130122 1653) nil "Jump to line number at point." tar ((:commit . "01ef8c3529d85e6c59cc20840acbc4a8e8325bc8") (:authors ("ongaeshi")) (:maintainers ("ongaeshi")) (:maintainer "ongaeshi") (:keywords "jump" "line" "back" "file" "ruby" "csharp" "python" "perl"))]) (jump-tree . [(20171014 1551) nil "Treat position history as a tree" tar ((:commit . "282267dc6305889e31d46b405b7ad4dfe5923b66") (:authors ("Wen Yang" . "yangwen0228@foxmail.com")) (:maintainers ("Wen Yang" . "yangwen0228@foxmail.com")) (:maintainer "Wen Yang" . "yangwen0228@foxmail.com") (:keywords "convenience" "position" "jump" "tree") (:url . "https://github.com/yangwen0228/jump-tree"))]) (jumplist . [(20151120 345) ((cl-lib (0 5))) "Jump like vim jumplist or ex jumplist" tar ((:commit . "c482d137d95bc5e1bcd790cdbde25b7f729b2502") (:authors ("ganmacs <ganmacs_at_gmail.com>")) (:maintainers ("ganmacs <ganmacs_at_gmail.com>")) (:maintainer "ganmacs <ganmacs_at_gmail.com>") (:keywords "jumplist" "vim") (:url . "https://github.com/ganmacs/jumplist"))]) - (jupyter . [(20240418 1642) ((emacs (26)) (cl-lib (0 5)) (org (9 1 6)) (zmq (0 10 10)) (simple-httpd (1 5 0)) (websocket (1 9))) "Jupyter" tar ((:commit . "f1394d303be76a1fa44d4135b4f3ceab9387a16b") (:authors ("Nathaniel Nicandro" . "nathanielnicandro@gmail.com")) (:maintainers ("Nathaniel Nicandro" . "nathanielnicandro@gmail.com")) (:maintainer "Nathaniel Nicandro" . "nathanielnicandro@gmail.com") (:url . "https://github.com/emacs-jupyter/jupyter"))]) + (jupyter . [(20240420 1918) ((emacs (26)) (cl-lib (0 5)) (org (9 1 6)) (zmq (0 10 10)) (simple-httpd (1 5 0)) (websocket (1 9))) "Jupyter" tar ((:commit . "aec436af541549ccd02e23c066a6c497d1365f6b") (:authors ("Nathaniel Nicandro" . "nathanielnicandro@gmail.com")) (:maintainers ("Nathaniel Nicandro" . "nathanielnicandro@gmail.com")) (:maintainer "Nathaniel Nicandro" . "nathanielnicandro@gmail.com") (:url . "https://github.com/emacs-jupyter/jupyter"))]) (just-mode . [(20240424 1809) ((emacs (26 1))) "Justfile editing mode" tar ((:commit . "4c0df4cc4b8798f1a7e99fb78b79c4bf7eec12c1") (:authors ("Leon Barrett" . "leon@barrettnexus.com")) (:maintainers ("Leon Barrett" . "leon@barrettnexus.com")) (:maintainer "Leon Barrett" . "leon@barrettnexus.com") (:keywords "files" "languages" "tools") (:url . "https://github.com/leon-barrett/just-mode.el"))]) (justl . [(20240523 232) ((transient (0 1 0)) (emacs (27 1)) (s (1 2 0)) (f (0 20 0)) (inheritenv (0 2))) "Major mode for driving just files" tar ((:commit . "bc7d00966f95c7a9cb519eeeaa3f258f854a7342") (:authors ("Sibi Prabakaran")) (:maintainers ("Sibi Prabakaran")) (:maintainer "Sibi Prabakaran") (:keywords "just" "justfile" "tools" "processes") (:url . "https://github.com/psibi/justl.el"))]) (jvm-mode . [(20150422 708) ((dash (2 6 0)) (emacs (24))) "Monitor and manage your JVMs" tar ((:commit . "3355dbaf5b0185aadfbad24160399abb32c5bea0") (:authors ("Martin Trojer" . "martin.trojer@gmail.com")) (:maintainers ("Martin Trojer" . "martin.trojer@gmail.com")) (:maintainer "Martin Trojer" . "martin.trojer@gmail.com") (:keywords "convenience") (:url . "https://github.com/martintrojer/jvm-mode.el"))]) @@ -2901,7 +2902,7 @@ (keepass-mode . [(20211030 958) ((emacs (27))) "Mode for KeePass DB." tar ((:commit . "f432bb60f9f3bd027025140d723906dcabeefaef") (:authors ("Ignasi Fosch" . "natx@y10k.ws")) (:maintainer "Ignasi Fosch" . "natx@y10k.ws") (:keywords "data" "files" "tools") (:url . "https://github.com/ifosch/keepass-mode"))]) (keg . [(20230709 1321) ((emacs (24 1))) "Modern Elisp package development system" tar ((:commit . "c0d24fdad4248e0291685b47a02df54e9f980aba") (:authors ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainers ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "Naoya Yamashita" . "conao3@gmail.com") (:keywords "convenience") (:url . "https://github.com/conao3/keg.el"))]) (keg-mode . [(20220307 829) ((emacs (24 4))) "Major mode for editing Keg files" tar ((:commit . "d2ef9cfaee1256849291cfade3d730667f55aaf2") (:authors ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainers ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "Naoya Yamashita" . "conao3@gmail.com") (:keywords "convenience") (:url . "https://github.com/conao3/keg.el"))]) - (kele . [(20240602 1257) ((emacs (28 1)) (async (1 9 7)) (dash (2 19 1)) (f (0 20 0)) (ht (2 3)) (memoize (0)) (plz (0 7 3)) (s (1 13 0)) (yaml (0 5 1))) "Spritzy Kubernetes cluster management" tar ((:commit . "294a75a476b1af7a7bd5c7fa46270741cee2fef3") (:authors ("Jonathan Jin" . "me@jonathanj.in")) (:maintainers ("Jonathan Jin" . "me@jonathanj.in")) (:maintainer "Jonathan Jin" . "me@jonathanj.in") (:keywords "kubernetes" "tools") (:url . "https://github.com/jinnovation/kele.el"))]) + (kele . [(20240608 1241) ((emacs (29 1)) (async (1 9 7)) (dash (2 19 1)) (f (0 20 0)) (ht (2 3)) (memoize (0)) (plz (0 8 0)) (s (1 13 0)) (yaml (0 5 1))) "Spritzy Kubernetes cluster management" tar ((:commit . "d50dd190807fd466fe078afe1c156cc463ecc102") (:authors ("Jonathan Jin" . "me@jonathanj.in")) (:maintainers ("Jonathan Jin" . "me@jonathanj.in")) (:maintainer "Jonathan Jin" . "me@jonathanj.in") (:keywords "kubernetes" "tools") (:url . "https://github.com/jinnovation/kele.el"))]) (kerl . [(20150424 2005) nil "Emacs integration for kerl" tar ((:commit . "1732ee26213f021bf040919c45ad276aafcaae14") (:authors ("Correl Roush" . "correl@gmail.com")) (:maintainers ("Correl Roush" . "correl@gmail.com")) (:maintainer "Correl Roush" . "correl@gmail.com") (:keywords "tools") (:url . "http://github.com/correl/kerl.el/"))]) (key-assist . [(20231208 446) ((emacs (24 3))) "Minibuffer keybinding cheatsheet and launcher" tar ((:commit . "87d2378db3d997b6b5a7b2c04281c18378e70bbb") (:authors ("Boruch Baum" . "boruch_baum@gmx.com")) (:maintainers ("Boruch Baum" . "boruch_baum@gmx.com")) (:maintainer "Boruch Baum" . "boruch_baum@gmx.com") (:keywords "abbrev" "convenience" "docs" "help") (:url . "https://github.com/Boruch-Baum/emacs-key-assist"))]) (key-chord . [(20240109 1430) ((emacs (24))) "map pairs of simultaneously pressed keys to commands" tar ((:commit . "dbf91fefdad58b1c2f07c92e658ce81490837c60") (:authors ("David Andersson <l.david.andersson(at)sverige.nu>")) (:maintainers ("David Andersson <l.david.andersson(at)sverige.nu>")) (:maintainer "David Andersson <l.david.andersson(at)sverige.nu>") (:keywords "keyboard" "chord" "input"))]) @@ -2910,7 +2911,7 @@ (key-leap . [(20160831 1447) ((emacs (24 3))) "Leap between lines by typing keywords" tar ((:commit . "b3f6ef15c8a13870475d5af159fa24b30f97dea0") (:authors ("Martin Rykfors" . "martinrykfors@gmail.com")) (:maintainers ("Martin Rykfors" . "martinrykfors@gmail.com")) (:maintainer "Martin Rykfors" . "martinrykfors@gmail.com") (:keywords "point" "convenience") (:url . "https://github.com/MartinRykfors/key-leap"))]) (key-quiz . [(20200226 2129) ((emacs (26))) "Emacs Keys Quiz" tar ((:commit . "1ee67f3f8977d95785e021f7896685de1979137e") (:authors ("Federico Tedin" . "federicotedin@gmail.com")) (:maintainers ("Federico Tedin" . "federicotedin@gmail.com")) (:maintainer "Federico Tedin" . "federicotedin@gmail.com") (:keywords "games") (:url . "https://github.com/federicotdn/key-quiz"))]) (key-seq . [(20150907 756) ((key-chord (0 6))) "map pairs of sequentially pressed keys to commands" tar ((:commit . "e29b083a6427d061638749194fc249ef69ad2cc0") (:authors ("Vyacheslav Levit" . "dev@vlevit.org")) (:maintainers ("Vyacheslav Levit" . "dev@vlevit.org")) (:maintainer "Vyacheslav Levit" . "dev@vlevit.org") (:keywords "convenience" "keyboard" "keybindings") (:url . "http://github.com/vlevit/key-seq.el"))]) - (keycast . [(20240415 1539) ((emacs (25 3)) (compat (29 1 4 1))) "Show current command and its binding" tar ((:commit . "04fa2c65f0ae901ed3015f691ea70f7658ea24b8") (:authors ("Jonas Bernoulli" . "emacs.keycast@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.keycast@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.keycast@jonas.bernoulli.dev") (:keywords "multimedia") (:url . "https://github.com/tarsius/keycast"))]) + (keycast . [(20240609 1438) ((emacs (25 3)) (compat (29 1 4 1))) "Show current command and its binding" tar ((:commit . "c156fd1c5ee02fc6395dcf10cb55cbd6fc5296a9") (:authors ("Jonas Bernoulli" . "emacs.keycast@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.keycast@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.keycast@jonas.bernoulli.dev") (:keywords "multimedia") (:url . "https://github.com/tarsius/keycast"))]) (keychain-environment . [(20180318 2223) nil "load keychain environment variables" tar ((:commit . "d3643196de6dc79ea77f9f4805028350fd76100b") (:authors ("Paul Tipper <bluefoo at googlemail dot com>")) (:maintainers ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "gnupg" "pgp" "ssh") (:url . "https://github.com/tarsius/keychain-environment"))]) (keydef . [(20090428 1931) nil "a simpler way to define keys, with kbd syntax" tar ((:commit . "dff2be9f58d12d8c6a490ad0c1b2b10b55528dc0") (:authors ("Michael John Downes" . "mjd@ams.org")) (:maintainers ("Michael John Downes" . "mjd@ams.org")) (:maintainer "Michael John Downes" . "mjd@ams.org") (:keywords "convenience" "lisp" "customization" "keyboard" "keys"))]) (keyfreq . [(20231107 106) ((cl-lib (0 5))) "track command frequencies" tar ((:commit . "c6955162307f37c2ac631d9daf118781009f8dda") (:authors ("Ryan Yeske, Michal Nazarewicz (mina86/AT/mina86.com)")) (:maintainers ("David Capello, Xah lee")) (:maintainer "David Capello, Xah lee"))]) @@ -2925,7 +2926,7 @@ (kfg . [(20140909 538) ((f (0 17 1))) "an emacs configuration system" tar ((:commit . "ffc35b77f227d4c64a1271ec30d31333ffeb0013") (:authors ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainers ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainer "Austin Bingham" . "austin.bingham@gmail.com") (:url . "https://github.com/abingham/kfg"))]) (khalel . [(20240527 527) ((emacs (27 1))) "Import, edit and create calendar events through khal" tar ((:commit . "14ef50352394cd1d62b80bc17ab14f4f801f47cd") (:authors ("Hanno Perrey <http://gitlab.com/hperrey>")) (:maintainers ("Hanno Perrey" . "hanno@hoowl.se")) (:maintainer "Hanno Perrey" . "hanno@hoowl.se") (:keywords "event" "calendar" "ics" "khal") (:url . "https://gitlab.com/hperrey/khalel"))]) (khardel . [(20231126 1502) ((emacs (27 1)) (yaml-mode (0 0 13))) "Integrate with khard" tar ((:commit . "205e374b36252183a146a7a8f857bcf95a77edc3") (:authors ("Damien Cassou" . "damien@cassou.me")) (:maintainers ("Damien Cassou" . "damien@cassou.me")) (:maintainer "Damien Cassou" . "damien@cassou.me") (:url . "https://github.com/DamienCassou/khardel"))]) - (khoj . [(20240524 1612) ((emacs (27 1)) (transient (0 3 0)) (dash (2 19 1))) "AI copilot for your Second Brain" tar ((:commit . "e23c803cee238a95635c9b4ffbf0bde51276b2b4") (:authors ("Debanjum Singh Solanky" . "debanjum@khoj.dev") ("Saba Imran" . "saba@khoj.dev")) (:maintainers ("Debanjum Singh Solanky" . "debanjum@khoj.dev")) (:maintainer "Debanjum Singh Solanky" . "debanjum@khoj.dev") (:keywords "search" "chat" "org-mode" "outlines" "markdown" "pdf" "image") (:url . "https://github.com/khoj-ai/khoj/tree/master/src/interface/emacs"))]) + (khoj . [(20240605 1409) ((emacs (27 1)) (transient (0 3 0)) (dash (2 19 1))) "AI copilot for your Second Brain" tar ((:commit . "21987f60c74695618b404cb70aae5cd04d696158") (:authors ("Debanjum Singh Solanky" . "debanjum@khoj.dev") ("Saba Imran" . "saba@khoj.dev")) (:maintainers ("Debanjum Singh Solanky" . "debanjum@khoj.dev")) (:maintainer "Debanjum Singh Solanky" . "debanjum@khoj.dev") (:keywords "search" "chat" "org-mode" "outlines" "markdown" "pdf" "image") (:url . "https://github.com/khoj-ai/khoj/tree/master/src/interface/emacs"))]) (kibit-helper . [(20150508 1533) ((s (0 8)) (emacs (24))) "Conveniently use the Kibit Leiningen plugin from Emacs" tar ((:commit . "ec5f154db3bb0c838e86f527353f08644cede926") (:authors ("Jonas Enlund") ("James Elliott" . "james@brunchboy.com")) (:maintainers ("Jonas Enlund")) (:maintainer "Jonas Enlund") (:keywords "languages" "clojure" "kibit") (:url . "http://www.github.com/brunchboy/kibit-helper"))]) (kill-file-path . [(20230306 1041) ((emacs (26))) "Copy file name into kill ring" tar ((:commit . "5dcbce69cbae17665216a32dd20f27de54c62972") (:authors ("Adam Chyła" . "adam@chyla.org")) (:maintainers ("Adam Chyła" . "adam@chyla.org")) (:maintainer "Adam Chyła" . "adam@chyla.org") (:keywords "files") (:url . "https://github.com/chyla/kill-file-path/kill-file-path.el"))]) (kill-or-bury-alive . [(20230606 1503) ((emacs (24 4))) "Precise control over buffer killing" tar ((:commit . "16c393db6ad0c7e184af0a24d26b637e23543b1f") (:authors ("Mark Karpov" . "markkarpov92@gmail.com")) (:maintainers ("Mark Karpov" . "markkarpov92@gmail.com")) (:maintainer "Mark Karpov" . "markkarpov92@gmail.com") (:keywords "convenience") (:url . "https://github.com/mrkkrp/kill-or-bury-alive"))]) @@ -2966,7 +2967,7 @@ (kv . [(20140108 1534) nil "key/value data structure functions" tar ((:commit . "721148475bce38a70e0b678ba8aa923652e8900e") (:authors ("Nic Ferrier" . "nferrier@ferrier.me.uk")) (:maintainers ("Nic Ferrier" . "nferrier@ferrier.me.uk")) (:maintainer "Nic Ferrier" . "nferrier@ferrier.me.uk") (:keywords "lisp"))]) (kwin . [(20220120 2125) nil "communicatewith the KWin window manager" tar ((:commit . "20fac6508e5535a26df783ba05f04d1800b7382c") (:authors ("Simon Hafner")) (:maintainers ("Simon Hafner")) (:maintainer "Simon Hafner") (:url . "http://github.com/reactormonk/kwin-minor-mode"))]) (laas . [(20230331 1806) ((emacs (26 3)) (auctex (11 88)) (aas (1 1))) "A bundle of as-you-type LaTeX snippets" tar ((:commit . "a00f0aba237b85b3e5fd60cf84de5759d1bf5d48") (:maintainers ("Yoav Marco" . "yoavm448@gmail.com")) (:maintainer "Yoav Marco" . "yoavm448@gmail.com") (:keywords "tools" "tex") (:url . "https://github.com/tecosaur/LaTeX-auto-activating-snippets"))]) - (lab . [(20240517 1837) ((emacs (27 1)) (memoize (1 1)) (request (0 3 2)) (s (1 10 0)) (f (0 20 0)) (compat (29 1 4 4)) (promise (1 1)) (async-await (1 1))) "An interface for GitLab" tar ((:commit . "fec1d5ad4e09c89c7260dc440f2dce6692b1ec0f") (:authors ("Isa Mert Gurbuz" . "isamertgurbuz@gmail.com")) (:maintainers ("Isa Mert Gurbuz" . "isamertgurbuz@gmail.com")) (:maintainer "Isa Mert Gurbuz" . "isamertgurbuz@gmail.com") (:url . "https://github.com/isamert/lab.el"))]) + (lab . [(20240606 737) ((emacs (27 1)) (memoize (1 1)) (request (0 3 2)) (s (1 10 0)) (f (0 20 0)) (compat (29 1 4 4)) (promise (1 1)) (async-await (1 1))) "An interface for GitLab" tar ((:commit . "097cee132fa57530da947a5a24fec0bd6510bb69") (:authors ("Isa Mert Gurbuz" . "isamertgurbuz@gmail.com")) (:maintainers ("Isa Mert Gurbuz" . "isamertgurbuz@gmail.com")) (:maintainer "Isa Mert Gurbuz" . "isamertgurbuz@gmail.com") (:url . "https://github.com/isamert/lab.el"))]) (lab-themes . [(20200815 2104) ((emacs (24))) "A custom theme carefully constructed in the LAB space" tar ((:commit . "9d7deb9635959d3a50ccb1082eb1207275f4b3e8") (:authors ("MetroWind" . "chris.corsair@gmail.com")) (:maintainers ("MetroWind" . "chris.corsair@gmail.com")) (:maintainer "MetroWind" . "chris.corsair@gmail.com") (:keywords "lisp") (:url . "https://github.com/MetroWind/lab-theme"))]) (labburn-theme . [(20221208 1611) nil "A lab color space zenburn theme." tar ((:commit . "bd0de2fdcf285d981f32e3e5ebc56fe3c9b589a5") (:authors ("Johannes Goslar")) (:maintainers ("Johannes Goslar")) (:maintainer "Johannes Goslar") (:keywords "theme" "zenburn") (:url . "https://github.com/ksjogo/labburn-theme"))]) (lacquer . [(20230824 725) ((emacs (25 2))) "Switch theme/font by selecting from a cache" tar ((:commit . "c8a0fb81f18001b3d510f545ba253ed4f9a50f5b") (:authors ("zakudriver" . "zy.hua1122@gmail.com")) (:maintainers ("zakudriver" . "zy.hua1122@gmail.com")) (:maintainer "zakudriver" . "zy.hua1122@gmail.com") (:keywords "tools") (:url . "https://github.com/zakudriver/lacquer"))]) @@ -3052,10 +3053,10 @@ (lines-at-once . [(20180422 247) ((emacs (25))) "Insert and edit multiple lines at once" tar ((:commit . "a018ba90549384d52ec58c2685fd14a0f65252be") (:authors ("Jiahao Li" . "jiahaowork@gmail.com")) (:maintainers ("Jiahao Li" . "jiahaowork@gmail.com")) (:maintainer "Jiahao Li" . "jiahaowork@gmail.com") (:keywords "abbrev" "tools") (:url . "https://github.com/jiahaowork/lines-at-once.el"))]) (lingr . [(20100807 1731) nil "Lingr Client for GNU Emacs" tar ((:commit . "4215a8704492d3c860097cbe2649936c22c196df") (:authors ("lugecy" . "lugecy@gmail.com")) (:maintainers ("lugecy" . "lugecy@gmail.com")) (:maintainer "lugecy" . "lugecy@gmail.com") (:keywords "chat" "client" "internet") (:url . "http://github.com/lugecy/lingr-el"))]) (linguistic . [(20181129 2116) nil "A package for basic linguistic analysis." tar ((:commit . "23e47e98cdb09ee61883669b6d8a11bf6449862c") (:authors ("Andrew Favia <drewlinguistics01 at gmail dot com>")) (:maintainers ("Andrew Favia <drewlinguistics01 at gmail dot com>")) (:maintainer "Andrew Favia <drewlinguistics01 at gmail dot com>") (:keywords "linguistics" "text analysis" "matching") (:url . "https://github.com/andcarnivorous/linguistic"))]) - (lingva . [(20220910 1435) ((emacs (25 1))) "Access Google Translate without tracking via lingva.ml" tar ((:commit . "6c33594068fa33de622172503deeec6778d9c744") (:authors ("marty hiatt <martianhiatus [a t] riseup [d o t] net>")) (:maintainers ("marty hiatt <martianhiatus [a t] riseup [d o t] net>")) (:maintainer "marty hiatt <martianhiatus [a t] riseup [d o t] net>") (:keywords "convenience" "translation" "wp" "text") (:url . "https://codeberg.org/martianh/lingva.el"))]) + (lingva . [(20240607 1120) ((emacs (25 1))) "Access Google Translate without tracking via lingva.ml" tar ((:commit . "c4cd68fb3ab1ebf419be0ec92b77d9feac921a87") (:authors ("marty hiatt <martianhiatus [a t] riseup [d o t] net>")) (:maintainers ("marty hiatt <martianhiatus [a t] riseup [d o t] net>")) (:maintainer "marty hiatt <martianhiatus [a t] riseup [d o t] net>") (:keywords "convenience" "translation" "wp" "text") (:url . "https://codeberg.org/martianh/lingva.el"))]) (link . [(20191111 446) nil "Hypertext links in text buffers" tar ((:commit . "c9cad101100975e88873636bfd426b7a19304ebd") (:authors ("Torsten Hilbrich" . "torsten.hilbrich@gmx.net")) (:maintainers ("Torsten Hilbrich" . "torsten.hilbrich@gmx.net")) (:maintainer "Torsten Hilbrich" . "torsten.hilbrich@gmx.net") (:keywords "interface" "hypermedia"))]) (link-hint . [(20240409 1250) ((avy (0 4 0)) (emacs (24 4))) "Use avy to open, copy, etc. visible links" tar ((:commit . "9ead085e9e6798ec4ea4791d9906d6655ea2b402") (:authors ("Fox Kiester" . "noct@posteo.net")) (:maintainers ("Fox Kiester" . "noct@posteo.net")) (:maintainer "Fox Kiester" . "noct@posteo.net") (:keywords "convenience" "url" "avy" "link" "links" "hyperlink") (:url . "https://github.com/noctuid/link-hint.el"))]) - (linkode . [(20200607 2152) nil "Generate a linkode snippet with region/buffer content" tar ((:commit . "e31bdae11ff38b736b1869fbe94920e862f29794") (:authors ("Erick Navarro" . "erick@navarro.io")) (:maintainers ("Erick Navarro" . "erick@navarro.io")) (:maintainer "Erick Navarro" . "erick@navarro.io") (:url . "https://github.com/erickgnavar/linkode.el"))]) + (linkode . [(20240604 53) nil "Generate a linkode snippet with region/buffer content" tar ((:commit . "5152aa3ba7a4360133efd5892f0891837af30440") (:authors ("Erick Navarro" . "erick@navarro.io")) (:maintainers ("Erick Navarro" . "erick@navarro.io")) (:maintainer "Erick Navarro" . "erick@navarro.io") (:url . "https://github.com/erickgnavar/linkode.el"))]) (linphone . [(20130524 1109) nil "Emacs interface to Linphone" tar ((:commit . "99af3db941b7f4e5272bb48bff96c1ce4ceac302") (:authors ("Yoni Rabkin" . "yonirabkin@member.fsf.org")) (:maintainers ("Yoni Rabkin" . "yonirabkin@member.fsf.org")) (:maintainer "Yoni Rabkin" . "yonirabkin@member.fsf.org") (:keywords "comm") (:url . "https://github.com/zabbal/emacs-linphone"))]) (linum-off . [(20160217 2137) nil "Provides an interface for turning line-numbering off" tar ((:commit . "3e37baaad27d27e405f8dfe01d4ab9cd5b591353") (:authors ("Matthew L. Fidler, Florian Adamsky (see wiki)")) (:maintainers ("Matthew L. Fidler")) (:maintainer "Matthew L. Fidler") (:keywords "line" "numbering") (:url . "http://www.emacswiki.org/emacs/auto-indent-mode.el "))]) (linum-relative . [(20221025 517) nil "display relative line number in emacs." tar ((:commit . "8fbe89ad897921849665a3e8da18cee7d0721441") (:authors ("coldnew" . "coldnew.tw@gmail.com")) (:maintainers ("coldnew" . "coldnew.tw@gmail.com")) (:maintainer "coldnew" . "coldnew.tw@gmail.com") (:keywords "converience") (:url . "http://github.com/coldnew/linum-relative"))]) @@ -3092,7 +3093,7 @@ (livescript-mode . [(20221015 1316) ((emacs (24 3))) "Major mode for editing LiveScript files" tar ((:commit . "e71a82a400e9d451c966c397bb8fa7887d35637b") (:authors ("Hisamatsu Yasuyuki" . "yas@null.net")) (:maintainers ("Hisamatsu Yasuyuki" . "yas@null.net")) (:maintainer "Hisamatsu Yasuyuki" . "yas@null.net") (:keywords "languages" "livescript") (:url . "https://github.com/yhisamatsu/livescript-mode"))]) (livid-mode . [(20131116 1344) ((skewer-mode (1 5 3)) (s (1 8 0))) "Live browser eval of JavaScript every time a buffer changes" tar ((:commit . "dfe5212fa64738bc4138bfebf349fbc8bc237c26") (:authors ("Murphy McMahon")) (:maintainers ("Murphy McMahon")) (:maintainer "Murphy McMahon") (:url . "https://github.com/pandeiro/livid-mode"))]) (ll-debug . [(20211002 1031) ((emacs (24 3))) "Low level debug tools" tar ((:commit . "a2cfeab46e5100c348b35987fae34f9ea76d7c0b") (:authors ("Claus Brunzema" . "mail@cbrunzema.de")) (:maintainers ("Claus Brunzema" . "mail@cbrunzema.de")) (:maintainer "Claus Brunzema" . "mail@cbrunzema.de") (:keywords "abbrev" "convenience" "tools" "c" "lisp") (:url . "https://github.com/replrep/ll-debug"))]) - (llama . [(20240520 1947) nil "Compact syntax for short lambda" tar ((:commit . "cea812d16129749deb524771e615a4a831604a83") (:keywords "extensions") (:url . "https://git.sr.ht/~tarsius/llama"))]) + (llama . [(20240609 1445) nil "Compact syntax for short lambda" tar ((:commit . "485949eb0122203b388a2de3a46a7a770a2cb622") (:keywords "extensions") (:url . "https://git.sr.ht/~tarsius/llama"))]) (llama-cpp . [(20240511 1039) ((emacs (27 1)) (dash (2 19 1))) "A client for llama-cpp server" tar ((:commit . "5cea3698aa63921b21888f126cae4f3ebc1baa39") (:authors ("Evgeny Kurnevsky" . "kurnevsky@gmail.com")) (:maintainers ("Evgeny Kurnevsky" . "kurnevsky@gmail.com")) (:maintainer "Evgeny Kurnevsky" . "kurnevsky@gmail.com") (:keywords "tools") (:url . "https://github.com/kurnevsky/llama.el"))]) (llvm-ts-mode . [(20231120 1251) ((emacs (29 1))) "LLVM major mode using tree-sitter" tar ((:commit . "9974601dcddbeffc4ad47598d63d3c1a83bb6fb9") (:authors ("Noah Peart" . "noah.v.peart@gmail.com")) (:maintainers ("Noah Peart" . "noah.v.peart@gmail.com")) (:maintainer "Noah Peart" . "noah.v.peart@gmail.com") (:keywords "languages" "tree-sitter" "llvm") (:url . "https://github.com/nverno/llvm-ts-mode"))]) (lms . [(20210820 2200) ((emacs (25 1))) "Squeezebox / Logitech Media Server frontend" tar ((:commit . "29593b4c18a570dfb2e60b196f24d407a1277daa") (:authors ("Iñigo Serna" . "inigoserna@gmx.com")) (:maintainers ("Iñigo Serna" . "inigoserna@gmx.com")) (:maintainer "Iñigo Serna" . "inigoserna@gmx.com") (:keywords "multimedia") (:url . "https://hg.serna.eu/emacs/lms"))]) @@ -3180,7 +3181,7 @@ (magic-filetype . [(20240130 1805) ((emacs (24 3)) (s (1 9 0))) "Enhance filetype major mode" tar ((:commit . "3979ddbd8066d7390e31bde2b35f997c5f5f4516") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainers ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "emulations" "vim" "ft" "file" "magic-mode") (:url . "https://github.com/emacs-php/magic-filetype.el"))]) (magic-latex-buffer . [(20210306 422) ((cl-lib (0 5)) (emacs (25 1))) "Magically enhance LaTeX-mode font-locking for semi-WYSIWYG editing" tar ((:commit . "903ec91872760e47c0e5715795f8465173615098") (:authors ("zk_phi")) (:maintainers ("zk_phi")) (:maintainer "zk_phi") (:url . "http://zk-phi.github.io/"))]) (magik-mode . [(20240521 1419) ((emacs (24 4)) (compat (28 1))) "Emacs major mode for Smallworld Magik files" tar ((:commit . "51ec2d21e4d68fc549d2022f86a882e090541ec3") (:keywords "languages") (:url . "https://github.com/roadrunner1776/magik"))]) - (magit . [(20240522 204) ((emacs (26 1)) (compat (29 1 4 5)) (dash (20240405)) (git-commit (20240429)) (magit-section (20240429)) (seq (2 24)) (transient (20240421)) (with-editor (20240415))) "A Git porcelain inside Emacs." tar ((:commit . "f9268a959828d0c6ab26171dd2fb1ffc55e5ae70") (:authors ("Marius Vollmer" . "marius.vollmer@gmail.com") ("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/magit"))]) + (magit . [(20240605 1500) ((emacs (26 1)) (compat (29 1 4 5)) (dash (20240405)) (git-commit (20240429)) (magit-section (20240429)) (seq (2 24)) (transient (20240421)) (with-editor (20240415))) "A Git porcelain inside Emacs." tar ((:commit . "8b2d4b03ecf9635c165d1c0f90cd6f2eb415cafa") (:authors ("Marius Vollmer" . "marius.vollmer@gmail.com") ("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/magit"))]) (magit-annex . [(20231210 2140) ((cl-lib (0 3)) (magit (3 0 0))) "Control git-annex from Magit" tar ((:commit . "056f0d4462cdccbd7bb7589994da7fef9de766da") (:authors ("Kyle Meyer" . "kyle@kyleam.com") ("Rémi Vanicat" . "vanicat@debian.org")) (:maintainers ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainer "Kyle Meyer" . "kyle@kyleam.com") (:keywords "vc" "tools") (:url . "https://github.com/magit/magit-annex"))]) (magit-commit-mark . [(20240421 931) ((emacs (29 1)) (magit (3 3 0))) "Support marking commits as read" tar ((:commit . "d09d0df6f8a697446e9fac77428b32997b94c59e") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainers ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-magit-commit-mark"))]) (magit-delta . [(20220125 50) ((emacs (25 1)) (magit (20200426)) (xterm-color (2 0))) "Use Delta when displaying diffs in Magit" tar ((:commit . "5fc7dbddcfacfe46d3fd876172ad02a9ab6ac616") (:authors ("Dan Davison" . "dandavison7@gmail.com")) (:maintainers ("Dan Davison" . "dandavison7@gmail.com")) (:maintainer "Dan Davison" . "dandavison7@gmail.com") (:url . "https://github.com/dandavison/magit-delta"))]) @@ -3287,7 +3288,7 @@ (memolist . [(20150804 1721) ((markdown-mode (22 0)) (ag (0 45))) "memolist.el is Emacs port of memolist.vim." tar ((:commit . "60c296e202a71e9dcf1c3936d47b5c4b95c5839f") (:authors ("mikanfactory <k952i4j14x17_at_gmail.com>")) (:maintainers ("mikanfactory")) (:maintainer "mikanfactory") (:keywords "markdown" "memo") (:url . "http://github.com/mikanfactory/emacs-memolist"))]) (mentor . [(20230103 1146) ((emacs (25 1)) (xml-rpc (1 6 15)) (seq (1 11)) (async (1 9 3)) (url-scgi (0 8))) "Frontend for the rTorrent bittorrent client" tar ((:commit . "f51dd4f3f87c54b7cc92189924b9d873a53f5a75") (:authors ("Stefan Kangas" . "stefankangas@gmail.com")) (:maintainers ("Stefan Kangas" . "stefankangas@gmail.com")) (:maintainer "Stefan Kangas" . "stefankangas@gmail.com") (:keywords "comm" "processes" "bittorrent") (:url . "https://github.com/skangas/mentor"))]) (meow . [(20240602 504) ((emacs (27 1))) "Yet Another modal editing" tar ((:commit . "f1bfad9518c2756375e16cd3f9f38235c3f57df8") (:authors ("Shi Tianshu")) (:maintainers ("Shi Tianshu")) (:maintainer "Shi Tianshu") (:keywords "convenience" "modal-editing") (:url . "https://www.github.com/DogLooksGood/meow"))]) - (merlin . [(20231201 918) ((emacs (25 1))) "Mode for Merlin, an assistant for OCaml" tar ((:commit . "ad9955c76b1cb031e847e139c5cf7b7cc5cb4696") (:authors ("Frédéric Bour <frederic.bour(_)lakaban.net>")) (:maintainers ("Frédéric Bour <frederic.bour(_)lakaban.net>")) (:maintainer "Frédéric Bour <frederic.bour(_)lakaban.net>") (:keywords "ocaml" "languages") (:url . "https://github.com/ocaml/merlin"))]) + (merlin . [(20240604 1521) ((emacs (25 1))) "Mode for Merlin, an assistant for OCaml" tar ((:commit . "bd900fd6cda97cfd10f2bf520848d0a8b0fe35b7") (:authors ("Frédéric Bour <frederic.bour(_)lakaban.net>")) (:maintainers ("Frédéric Bour <frederic.bour(_)lakaban.net>")) (:maintainer "Frédéric Bour <frederic.bour(_)lakaban.net>") (:keywords "ocaml" "languages") (:url . "https://github.com/ocaml/merlin"))]) (merlin-ac . [(20221123 1408) ((emacs (25 1)) (merlin (3)) (auto-complete (1 5))) "Merlin and auto-complete integration" tar ((:commit . "8bcab034a680f57ddf58092fda6288dc4caddd2a") (:authors ("Simon Castellan <simon.castellan(_)iuwt.fr>") ("Frédéric Bour <frederic.bour(_)lakaban.net>") ("Thomas Refis <thomas.refis(_)gmail.com>")) (:maintainers ("Simon Castellan <simon.castellan(_)iuwt.fr>")) (:maintainer "Simon Castellan <simon.castellan(_)iuwt.fr>") (:keywords "ocaml" "languages") (:url . "http://github.com/ocaml/merlin"))]) (merlin-company . [(20221123 1408) ((emacs (25 1)) (merlin (3)) (company (0 9))) "Merlin and company mode integration" tar ((:commit . "8bcab034a680f57ddf58092fda6288dc4caddd2a") (:authors ("Simon Castellan <simon.castellan(_)iuwt.fr>") ("Frédéric Bour <frederic.bour(_)lakaban.net>") ("Thomas Refis <thomas.refis(_)gmail.com>")) (:maintainers ("Simon Castellan <simon.castellan(_)iuwt.fr>")) (:maintainer "Simon Castellan <simon.castellan(_)iuwt.fr>") (:keywords "ocaml" "languages") (:url . "http://github.com/ocaml/merlin"))]) (merlin-eldoc . [(20230213 555) ((emacs (24 4)) (merlin (3 0))) "eldoc for OCaml and Reason" tar ((:commit . "bf8edc63d85b35e4def352fa7ce4ea39f43e1fd8") (:authors ("Louis Roché" . "louis@louisroche.net")) (:maintainers ("Louis Roché" . "louis@louisroche.net")) (:maintainer "Louis Roché" . "louis@louisroche.net") (:keywords "merlin" "ocaml" "languages" "eldoc") (:url . "https://github.com/khady/merlin-eldoc"))]) @@ -3325,7 +3326,7 @@ (mindstream . [(20240519 1715) ((emacs (25 1)) (magit (3 3 0))) "Start writing, stay focused, don't worry" tar ((:commit . "ae8b10b077d71de5e8ad966e03527e8d55b3e18d") (:authors ("Siddhartha Kasivajhula" . "sid@countvajhula.com")) (:maintainers ("Siddhartha Kasivajhula" . "sid@countvajhula.com")) (:maintainer "Siddhartha Kasivajhula" . "sid@countvajhula.com") (:keywords "convenience" "files" "languages" "outlines" "tools" "vc" "wp") (:url . "https://github.com/countvajhula/mindstream"))]) (minesweeper . [(20200416 2342) nil "play minesweeper in Emacs" tar ((:commit . "d4248e3c9b3e9e7277cb9e6d081330611898f334") (:authors ("Zachary Kanfer" . "zkanfer@gmail.com")) (:maintainers ("Zachary Kanfer" . "zkanfer@gmail.com")) (:maintainer "Zachary Kanfer" . "zkanfer@gmail.com") (:keywords "game" "fun" "minesweeper" "inane" "diversion") (:url . "https://hg.sr.ht/~zck/minesweeper"))]) (mingus . [(20230518 1726) ((libmpdee (2 2))) "MPD Interface" tar ((:commit . "3fa9b95552eb062eb245321abb7f442c458618dc") (:authors ("Niels Giesen <pft on #emacs>")) (:maintainers ("Niels Giesen <pft on #emacs>")) (:maintainer "Niels Giesen <pft on #emacs>") (:keywords "multimedia" "elisp" "music" "mpd") (:url . "https://github.com/pft/mingus"))]) - (mini-echo . [(20240602 1242) ((emacs (29 1)) (dash (2 19 1)) (hide-mode-line (1 0 3))) "Echo buffer status in minibuffer window" tar ((:commit . "6d419f8b118fa2858da3563e2dfeadcbc51e8902") (:authors ("liuyinz" . "liuyinz95@gmail.com")) (:maintainers ("liuyinz" . "liuyinz95@gmail.com")) (:maintainer "liuyinz" . "liuyinz95@gmail.com") (:keywords "frames") (:url . "https://github.com/liuyinz/mini-echo.el"))]) + (mini-echo . [(20240603 424) ((emacs (29 1)) (dash (2 19 1)) (hide-mode-line (1 0 3))) "Echo buffer status in minibuffer window" tar ((:commit . "b556c0fa68dad76a478bd3f508e0501aa6933689") (:authors ("liuyinz" . "liuyinz95@gmail.com")) (:maintainers ("liuyinz" . "liuyinz95@gmail.com")) (:maintainer "liuyinz" . "liuyinz95@gmail.com") (:keywords "frames") (:url . "https://github.com/liuyinz/mini-echo.el"))]) (mini-frame . [(20220627 2041) ((emacs (26 1))) "Show minibuffer in child frame on read-from-minibuffer" tar ((:commit . "60838f3cab438dcbda8eaa15ab3e5d1af88910e9") (:authors ("Andrii Kolomoiets" . "andreyk.mad@gmail.com")) (:maintainers ("Andrii Kolomoiets" . "andreyk.mad@gmail.com")) (:maintainer "Andrii Kolomoiets" . "andreyk.mad@gmail.com") (:keywords "frames") (:url . "https://github.com/muffinmad/emacs-mini-frame"))]) (mini-header-line . [(20170621 1221) ((emacs (24 4))) "a minimal header-line" tar ((:commit . "73b6724e0a26c4528d93768191c8aa59e6bce2e5") (:authors ("Johannes Goslar")) (:maintainers ("Johannes Goslar")) (:maintainer "Johannes Goslar") (:keywords "header-line" "mode-line") (:url . "https://github.com/ksjogo/mini-header-line"))]) (mini-modeline . [(20230306 1521) ((emacs (25 1)) (dash (2 12 0))) "Display modeline in minibuffer" tar ((:commit . "86e753b6c38a06b0fc80d7560aa6a25245fd4d38") (:authors ("Kien Nguyen" . "kien.n.quang@gmail.com")) (:maintainers ("Kien Nguyen" . "kien.n.quang@gmail.com")) (:maintainer "Kien Nguyen" . "kien.n.quang@gmail.com") (:keywords "convenience" "tools") (:url . "https://github.com/kiennq/emacs-mini-modeline"))]) @@ -3349,7 +3350,7 @@ (mix . [(20240122 720) ((emacs (25 1))) "Mix Major Mode. Build Elixir using Mix" tar ((:commit . "16cc69cbf919769c191b1c49c1cab324fd0682a9") (:authors ("Ayrat Badykov" . "ayratin555@gmail.com")) (:maintainers ("Ayrat Badykov" . "ayratin555@gmail.com")) (:maintainer "Ayrat Badykov" . "ayratin555@gmail.com") (:keywords "tools") (:url . "https://github.com/ayrat555/mix.el"))]) (mixed-pitch . [(20210304 1900) ((emacs (24 3))) "Use a variable pitch, keeping fixed pitch where it's sensible" tar ((:commit . "519e05f74825abf04b7d2e0e38ec040d013a125a") (:authors ("J. Alexander Branham" . "branham@utexas.edu")) (:maintainers ("J. Alexander Branham" . "branham@utexas.edu")) (:maintainer "J. Alexander Branham" . "branham@utexas.edu") (:url . "https://gitlab.com/jabranham/mixed-pitch"))]) (mkdown . [(20140517 1418) ((markdown-mode (2 0))) "Pretty Markdown previews based on mkdown.com" tar ((:commit . "8e23de82719af6c5b53b52b3308a02b3a1fb872e") (:authors ("Andrew Tulloch")) (:maintainers ("Andrew Tulloch")) (:maintainer "Andrew Tulloch") (:keywords "markdown") (:url . "https://github.com/ajtulloch/mkdown.el"))]) - (mlscroll . [(20240528 2006) ((emacs (27 1))) "A scroll bar for the modeline" tar ((:commit . "b11eda812eb4eabf77af749ca6ae8327157c2d0b") (:authors ("J.D. Smith")) (:maintainers ("J.D. Smith")) (:maintainer "J.D. Smith") (:keywords "convenience") (:url . "https://github.com/jdtsmith/mlscroll"))]) + (mlscroll . [(20240606 1855) ((emacs (27 1))) "A scroll bar for the modeline" tar ((:commit . "805d913771270f8157730f634108a237ca03137e") (:authors ("J.D. Smith")) (:maintainers ("J.D. Smith")) (:maintainer "J.D. Smith") (:keywords "convenience") (:url . "https://github.com/jdtsmith/mlscroll"))]) (mmm-jinja2 . [(20170313 1420) ((mmm-mode (0 5 4))) "MMM submode class for Jinja2 Templates" tar ((:commit . "c8cb763174fa2fb61b9a0e5e0ff8cb0210f8492f") (:authors ("Ben Hayden" . "hayden767@gmail.com")) (:maintainers ("Ben Hayden" . "hayden767@gmail.com")) (:maintainer "Ben Hayden" . "hayden767@gmail.com") (:url . "https://github.com/glynnforrest/mmm-jinja2"))]) (mmm-mode . [(20240222 428) ((emacs (25 1)) (cl-lib (0 2))) "Allow Multiple Major Modes in a buffer" tar ((:commit . "b1f5c7dbdc405e6e10d9ddd99a43a6b2ad61b176") (:authors ("Michael Abraham Shulman" . "viritrilbia@gmail.com")) (:maintainers ("Dmitry Gutov" . "dmitry@gutov.dev")) (:maintainer "Dmitry Gutov" . "dmitry@gutov.dev") (:keywords "convenience" "faces" "languages" "tools") (:url . "https://github.com/dgutov/mmm-mode"))]) (mmt . [(20230606 1513) ((emacs (24 5))) "Missing macro tools for Emacs Lisp" tar ((:commit . "2a24463eeb72ebef100e89977ebfb88f5f220217") (:authors ("Mark Karpov" . "markkarpov92@gmail.com")) (:maintainers ("Mark Karpov" . "markkarpov92@gmail.com")) (:maintainer "Mark Karpov" . "markkarpov92@gmail.com") (:keywords "macro" "lisp" "extensions") (:url . "https://github.com/mrkkrp/mmt"))]) @@ -3371,7 +3372,7 @@ (modern-sh . [(20211101 1001) ((emacs (25 1)) (hydra (0 15 0)) (eval-in-repl (0 9 7))) "Minor mode for editing shell script" tar ((:commit . "8ebebe77304aa8170f7af809e7564c79d3bd45da") (:keywords "languages" "programming") (:url . "https://github.com/damon-kwok/modern-sh"))]) (modtime-skip-mode . [(20140128 2201) nil "Minor mode for disabling modtime and supersession checks on files." tar ((:commit . "c0e49523aa26b2263a8693691ac775988015f592") (:authors ("Jordon Biondo" . "biondoj@mail.gvsu.edu")) (:maintainers ("Jordon Biondo" . "biondoj@mail.gvsu.edu")) (:maintainer "Jordon Biondo" . "biondoj@mail.gvsu.edu") (:url . "http://www.github.com/jordonbiondo/modtime-skip-mode"))]) (modular-config . [(20210726 1614) ((emacs (25 1))) "Organize your config into small and loadable modules" tar ((:commit . "043907d96efff70dfaea1e721de90bd35970e8bd") (:authors ("Sidharth Arya" . "sidhartharya10@gmail.com")) (:maintainers ("Sidharth Arya" . "sidhartharya10@gmail.com")) (:maintainer "Sidharth Arya" . "sidhartharya10@gmail.com") (:keywords "startup" "lisp" "tools") (:url . "https://github.com/SidharthArya/modular-config.el"))]) - (modus-themes . [(20240505 331) ((emacs (27 1))) "Elegant, highly legible and customizable themes" tar ((:commit . "d2762db19ed48bd0cbba61c41940be479760a35e") (:authors ("Protesilaos Stavrou" . "info@protesilaos.com")) (:maintainers ("Protesilaos Stavrou" . "info@protesilaos.com")) (:maintainer "Protesilaos Stavrou" . "info@protesilaos.com") (:keywords "faces" "theme" "accessibility") (:url . "https://github.com/protesilaos/modus-themes"))]) + (modus-themes . [(20240603 1554) ((emacs (27 1))) "Elegant, highly legible and customizable themes" tar ((:commit . "1090a80a76c77d215b948d68a707fbb7e2b8d407") (:authors ("Protesilaos Stavrou" . "info@protesilaos.com")) (:maintainers ("Protesilaos Stavrou" . "info@protesilaos.com")) (:maintainer "Protesilaos Stavrou" . "info@protesilaos.com") (:keywords "faces" "theme" "accessibility") (:url . "https://github.com/protesilaos/modus-themes"))]) (moe-theme . [(20240430 1601) nil "A colorful eye-candy theme. Moe, moe, kyun!" tar ((:commit . "6df0d99a1a2006b218282f15d84609b88478ca7a") (:authors ("kuanyui" . "azazabc123@gmail.com")) (:maintainers ("kuanyui" . "azazabc123@gmail.com")) (:maintainer "kuanyui" . "azazabc123@gmail.com") (:keywords "themes") (:url . "https://github.com/kuanyui/moe-theme.el"))]) (molar-mass . [(20220922 1752) ((emacs (24 3))) "Calculates molar mass of a molecule" tar ((:commit . "c3b686c4b621b45fa4b17857b4934eb4487d74f5") (:authors ("Sergi Ruiz Trepat")) (:maintainers ("Sergi Ruiz Trepat")) (:maintainer "Sergi Ruiz Trepat") (:keywords "convenience" "chemistry") (:url . "https://github.com/sergiruiztrepat/molar-mass.el"))]) (molecule . [(20180527 743) ((emacs (25 1))) "Simple wrapper for molecule" tar ((:commit . "2ef72b81d9aa24ea782b71a061a3abdad6cae162") (:authors (": drymer <drymer [ AT ] autistici.org>")) (:maintainers (": drymer <drymer [ AT ] autistici.org>")) (:maintainer ": drymer <drymer [ AT ] autistici.org>") (:keywords ":" "languages" "terminals") (:url . "https://git.daemons.it/drymer/molecule.el"))]) @@ -3470,7 +3471,7 @@ (mysql-to-org . [(20210622 447) ((emacs (24 3)) (s (1 11 0))) "Minor mode to output the results of mysql queries to org tables" tar ((:commit . "c5eefc71200f2e1d0d67a13ed897b3cdfa835117") (:authors ("Tijs Mallaerts" . "tijs.mallaerts@gmail.com")) (:maintainers ("Tijs Mallaerts" . "tijs.mallaerts@gmail.com")) (:maintainer "Tijs Mallaerts" . "tijs.mallaerts@gmail.com"))]) (myterminal-controls . [(20210904 516) ((emacs (24)) (cl-lib (0 5))) "Quick toggle controls at a key-stroke" tar ((:commit . "c635868e13ee898ec77925d98b36421640e22aa4") (:authors ("Mohammed Ismail Ansari" . "team.terminal@gmail.com")) (:maintainers ("Mohammed Ismail Ansari" . "team.terminal@gmail.com")) (:maintainer "Mohammed Ismail Ansari" . "team.terminal@gmail.com") (:keywords "convenience" "shortcuts") (:url . "http://ismail.teamfluxion.com"))]) (n4js . [(20150714 231) ((emacs (24)) (cypher-mode (0))) "Neo4j Shell" tar ((:commit . "3991ed8975151d5e8d568e952362df810f7ffab7") (:authors ("TruongTx" . "me@truongtx.me")) (:maintainers ("TruongTx" . "me@truongtx.me")) (:maintainer "TruongTx" . "me@truongtx.me") (:keywords "neo4j" "shell" "comint") (:url . "https://github.com/tmtxt/n4js.el"))]) - (naga-theme . [(20240327 819) nil "Dark color theme with green foreground color" tar ((:commit . "a044e5eb5bf9bbefe34982ae9cc80ac9739a3e58") (:authors ("Johannes Maier" . "johannes.maier@mailbox.org")) (:maintainers ("Johannes Maier" . "johannes.maier@mailbox.org")) (:maintainer "Johannes Maier" . "johannes.maier@mailbox.org"))]) + (naga-theme . [(20240607 1946) nil "Dark color theme with green foreground color" tar ((:commit . "84b28f3f5dcdd03205d5ff4764704806019ff332") (:authors ("Johannes Maier" . "johannes.maier@mailbox.org")) (:maintainers ("Johannes Maier" . "johannes.maier@mailbox.org")) (:maintainer "Johannes Maier" . "johannes.maier@mailbox.org"))]) (name-this-color . [(20151014 2030) ((emacs (24)) (cl-lib (0 5)) (dash (2 11 0))) "Match RGB codes to names easily and precisely" tar ((:commit . "e37cd1291d5d68d4c8d6386eab9cb9d94fd3bcfa") (:keywords "lisp" "color" "hex" "rgb" "shade" "name") (:url . "https://github.com/knl/name-this-color.el"))]) (named-timer . [(20181120 2224) ((emacs (24 4))) "Simplified timer management for Emacs Lisp" tar ((:commit . "670b81e3eddef2e7353a4eedc9553a85306445db") (:authors ("Ryan C. Thompson")) (:maintainers ("Ryan C. Thompson")) (:maintainer "Ryan C. Thompson") (:keywords "tools") (:url . "https://github.com/DarwinAwardWinner/emacs-named-timer"))]) (nameframe . [(20221023 957) nil "Manage frames by name." tar ((:commit . "06d3400750c6b33ae215b9ac2922ee4dafd6b506") (:authors ("John Del Rosario" . "john2x@gmail.com")) (:maintainers ("John Del Rosario" . "john2x@gmail.com")) (:maintainer "John Del Rosario" . "john2x@gmail.com") (:url . "https://github.com/john2x/nameframe"))]) @@ -3553,7 +3554,7 @@ (nntwitter . [(20230705 1110) ((emacs (25 1)) (dash (20190401)) (anaphora (20180618)) (request (20190819))) "Gnus Backend For Twitter" tar ((:commit . "e27acca9beeb6645dd13545d42f6d4d97d59d82c") (:keywords "news") (:url . "https://github.com/dickmao/nntwitter"))]) (no-clown-fiesta-theme . [(20231214 2115) ((emacs (26 1)) (autothemer (0 2))) "Not-so-colorful-theme" tar ((:commit . "0cd04a72aa5dcf61e82e2a613670334816326b02") (:authors ("ranmaru22")) (:maintainers ("ranmaru22")) (:maintainer "ranmaru22") (:url . "https://github.com/ranmaru22/no-clown-fiesta-theme.el"))]) (no-emoji . [(20180515 1837) ((emacs (24))) "Show :emoji-name: instead of emoji characters" tar ((:commit . "ebceeab50dbfe4d60235180a57633745dbc18c77") (:authors ("Peter" . "craven@gmx.net")) (:maintainers ("Peter" . "craven@gmx.net")) (:maintainer "Peter" . "craven@gmx.net") (:keywords "extensions") (:url . "https://github.com/ecraven/no-emoji"))]) - (no-littering . [(20240601 1254) ((emacs (25 1)) (compat (29 1 4 2))) "Help keeping ~/.config/emacs clean" tar ((:commit . "4a71c736ecf04f9b8e5bdd10a73ef1febb73a557") (:authors ("Jonas Bernoulli" . "emacs.no-littering@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.no-littering@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.no-littering@jonas.bernoulli.dev") (:keywords "convenience") (:url . "https://github.com/emacscollective/no-littering"))]) + (no-littering . [(20240609 1455) ((emacs (25 1)) (compat (29 1 4 2))) "Help keeping ~/.config/emacs clean" tar ((:commit . "046c8147896c0a0572668215508fd827bdb144ec") (:authors ("Jonas Bernoulli" . "emacs.no-littering@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.no-littering@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.no-littering@jonas.bernoulli.dev") (:keywords "convenience") (:url . "https://github.com/emacscollective/no-littering"))]) (no-spam . [(20190724 1854) ((emacs (25 1))) "Add repeat delays to commands" tar ((:commit . "860860e4a0d59bd15c8e092dc42f5f7f769a428e") (:authors ("Daniel Phan" . "daniel.phan36@gmail.com")) (:maintainers ("Daniel Phan" . "daniel.phan36@gmail.com")) (:maintainer "Daniel Phan" . "daniel.phan36@gmail.com") (:keywords "keyboard" "tools") (:url . "https://github.com/mamapanda/no-spam"))]) (noaa . [(20240317 2321) ((emacs (27 1)) (kv (0 0 19)) (request (0 2 0)) (s (1 12 0))) "Get NOAA weather data" tar ((:commit . "7d68b5a580c64123f3bbd75f795a891dfdeb1746") (:authors ("David Thompson")) (:maintainers ("David Thompson")) (:maintainer "David Thompson") (:keywords "calendar") (:url . "https://codeberg.org/thomp/noaa"))]) (noccur . [(20191015 719) nil "Run multi-occur on project/dired files" tar ((:commit . "fa91647a305e89561d3dbe53da002fff49abe0bb") (:authors ("Nicolas Petton" . "petton.nicolas@gmail.com")) (:maintainers ("Nicolas Petton" . "petton.nicolas@gmail.com")) (:maintainer "Nicolas Petton" . "petton.nicolas@gmail.com") (:keywords "convenience"))]) @@ -3574,9 +3575,9 @@ (norns . [(20230820 2157) ((emacs (27 1)) (dash (2 17 0)) (s (1 12 0)) (f (0 20 0)) (request (0 3 2)) (websocket (1 13)) (lua-mode (20221218 605))) "Interactive development environment for monome norns" tar ((:commit . "7e8b73b621db7494a22914b16c614ef80521f7f7") (:keywords "processes" "terminals") (:url . "https://github.com/p3r7/norns.el"))]) (northcode-theme . [(20180423 1649) ((emacs (24))) "A dark theme focused on blue and orange colors." tar ((:commit . "4d3750461ba25ec45321318b5f1af4e8fdf16147") (:authors ("Andreas Larsen" . "andreas@northcode.no")) (:maintainers ("Andreas Larsen" . "andreas@northcode.no")) (:maintainer "Andreas Larsen" . "andreas@northcode.no") (:url . "https://github.com/Northcode/northcode-theme.el"))]) (nothing-theme . [(20200504 402) ((emacs (24 1))) "Monochrome theme" tar ((:commit . "17fc9ecc94af0c919a24c4fe92bb48890bb4c3b0") (:authors ("Jared Gorski," . "jaredgorski6@gmail.com")) (:maintainers ("Jared Gorski," . "jaredgorski6@gmail.com")) (:maintainer "Jared Gorski," . "jaredgorski6@gmail.com") (:url . "https://github.com/jaredgorski/nothing.el"))]) - (notink-theme . [(20220114 1955) ((emacs (26 1))) "A custom theme inspired by e-ink displays" tar ((:commit . "6115857fe75c1adbbce4165a2b77a11a271aaf31") (:authors ("MetroWind" . "chris.corsair@gmail.com")) (:maintainers ("MetroWind" . "chris.corsair@gmail.com")) (:maintainer "MetroWind" . "chris.corsair@gmail.com") (:keywords "faces") (:url . "https://github.com/MetroWind/notink-theme"))]) + (notink-theme . [(20240605 2018) ((emacs (26 1))) "A custom theme inspired by e-ink displays" tar ((:commit . "b87954598ca78c4adabe17b7f23723657c3460e8") (:authors ("MetroWind" . "chris.corsair@gmail.com")) (:maintainers ("MetroWind" . "chris.corsair@gmail.com")) (:maintainer "MetroWind" . "chris.corsair@gmail.com") (:keywords "faces") (:url . "https://github.com/MetroWind/notink-theme"))]) (notmuch . [(20240529 1054) nil "run notmuch within emacs" tar ((:commit . "e8e2d5247e44d9b402cbcc491b859cd9398f045d") (:url . "https://notmuchmail.org"))]) - (notmuch-addr . [(20240422 15) ((emacs (27 1)) (compat (29 1 4 1)) (notmuch (0 37))) "An alternative to notmuch-address.el" tar ((:commit . "e5755c5a752d2026b1c847bc5944bdd5f015c1a6") (:authors ("Jonas Bernoulli" . "emacs.notmuch-addr@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.notmuch-addr@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.notmuch-addr@jonas.bernoulli.dev") (:keywords "mail") (:url . "https://git.sr.ht/~tarsius/notmuch-addr"))]) + (notmuch-addr . [(20240609 1455) ((emacs (27 1)) (compat (29 1 4 1)) (notmuch (0 37))) "An alternative to notmuch-address.el" tar ((:commit . "2ccfdae6c0a5dd1b6c57f4525614b7b3e6f0fb38") (:authors ("Jonas Bernoulli" . "emacs.notmuch-addr@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.notmuch-addr@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.notmuch-addr@jonas.bernoulli.dev") (:keywords "mail") (:url . "https://git.sr.ht/~tarsius/notmuch-addr"))]) (notmuch-bookmarks . [(20230727 1504) ((seq (2 20)) (emacs (26 1)) (notmuch (0 29 3))) "Add bookmark handling for notmuch buffers" tar ((:commit . "7c053fd2d278dc3a9f07f86975867bfbb4e7448d") (:authors ("Jörg Volbers" . "joerg@joergvolbers.de")) (:maintainers ("Jörg Volbers" . "joerg@joergvolbers.de")) (:maintainer "Jörg Volbers" . "joerg@joergvolbers.de") (:keywords "mail") (:url . "https://github.com/publicimageltd/notmuch-bookmarks"))]) (notmuch-labeler . [(20131230 1719) ((notmuch (0))) "Improve notmuch way of displaying labels" tar ((:commit . "d65d1129555d368243df4770ecc1e7ccb88efc58") (:authors ("Damien Cassou" . "damien.cassou@gmail.com")) (:maintainers ("Damien Cassou" . "damien.cassou@gmail.com")) (:maintainer "Damien Cassou" . "damien.cassou@gmail.com") (:keywords "emacs" "package" "elisp" "notmuch" "emails") (:url . "https://github.com/DamienCassou/notmuch-labeler"))]) (notmuch-maildir . [(20240415 1545) ((emacs (26 1)) (compat (29 1 4 1)) (notmuch (0 37))) "Visualize maildirs as a tree" tar ((:commit . "3a7240e6728731b427a763228c60104602f0fe4b") (:authors ("Jonas Bernoulli" . "emacs.notmuch-maildir@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.notmuch-maildir@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.notmuch-maildir@jonas.bernoulli.dev") (:keywords "mail") (:url . "https://git.sr.ht/~tarsius/notmuch-maildir"))]) @@ -3739,7 +3740,7 @@ (operate-on-number . [(20231114 1921) nil "Operate on number at point with arithmetic functions" tar ((:commit . "0ddebae1885c1b54eae1d79e66204d6d83c5065b") (:authors ("Akinori MUSHA" . "knu@iDaemons.org")) (:maintainers ("Akinori MUSHA" . "knu@iDaemons.org")) (:maintainer "Akinori MUSHA" . "knu@iDaemons.org") (:keywords "editing") (:url . "https://github.com/knu/operate-on-number.el"))]) (orangey-bits-theme . [(20220822 324) ((autothemer (0 2)) (emacs (27 1))) "A Theme with smashing orangey bits" tar ((:commit . "533856d399cb4098300bcaf4a2d20920395746f8") (:url . "http://github.com/emacsfodder/emacs-theme-orangey-bits"))]) (orca . [(20220828 4) ((emacs (24 3)) (zoutline (0 1 0))) "Org Capture" tar ((:commit . "0687f416a5573f63b691d384454f5a793266ed97") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainers ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "org" "convenience") (:url . "https://github.com/abo-abo/orca"))]) - (orderless . [(20240401 959) ((emacs (27 1))) "Completion style for matching regexps in any order" tar ((:commit . "ac4aeb66f331f4c4a430d5556071e33177304c37") (:authors ("Omar Antolín Camarena" . "omar@matem.unam.mx")) (:maintainers ("Omar Antolín Camarena <omar@matem.unam.mx>, Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainer "Omar Antolín Camarena <omar@matem.unam.mx>, Daniel Mendler" . "mail@daniel-mendler.de") (:keywords "extensions") (:url . "https://github.com/oantolin/orderless"))]) + (orderless . [(20240606 1026) ((emacs (27 1))) "Completion style for matching regexps in any order" tar ((:commit . "53f5204ad3f541e11eb6eeb9b86584964b7a3678") (:authors ("Omar Antolín Camarena" . "omar@matem.unam.mx")) (:maintainers ("Omar Antolín Camarena <omar@matem.unam.mx>, Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainer "Omar Antolín Camarena <omar@matem.unam.mx>, Daniel Mendler" . "mail@daniel-mendler.de") (:keywords "extensions") (:url . "https://github.com/oantolin/orderless"))]) (ordinal . [(20210519 1442) ((emacs (24 3))) "Convert number to ordinal number notation" tar ((:commit . "a7f378306290b6807fb6b87cee3ef79b31cec711") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainers ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "lisp") (:url . "https://github.com/zonuexe/ordinal.el"))]) (org-ac . [(20170401 1307) ((auto-complete-pcmp (0 0 1)) (log4e (0 2 0)) (yaxception (0 1))) "Some auto-complete sources for org-mode" tar ((:commit . "41e3ef8e4039619d0370c23c66730b3b2e9e32ed") (:authors ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainers ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainer "Hiroaki Otsu" . "ootsuhiroaki@gmail.com") (:keywords "org" "completion") (:url . "https://github.com/aki2o/org-ac"))]) (org-agenda-files-track . [(20231209 1529) ((emacs (27 1))) "Fine-track `org-agenda-files' to speed-up `org-agenda'" tar ((:commit . "c0f5f7746ec023a32ba106ec24812eca5cbe15df") (:authors ("Nicolas Graves" . "ngraves@ngraves.fr")) (:maintainers ("Nicolas Graves" . "ngraves@ngraves.fr")) (:maintainer "Nicolas Graves" . "ngraves@ngraves.fr") (:keywords "data" "files" "tools") (:url . "https://git.sr.ht/~ngraves/org-agenda-files-track"))]) @@ -3760,7 +3761,7 @@ (org-beautify-theme . [(20170908 2218) nil "A sub-theme to make org-mode more beautiful." tar ((:commit . "df6a1114fda313e1689363e196c8284fbe2a2738") (:authors ("Jonathan Arkell" . "jonnay@jonnay.net")) (:maintainers ("Jonathan Arkell" . "jonnay@jonnay.net")) (:maintainer "Jonathan Arkell" . "jonnay@jonnay.net") (:keywords "org" "theme"))]) (org-board . [(20230408 1041) nil "bookmarking and web archival system for Org mode." tar ((:commit . "500fe02bc114e5b535a2eb2ab73954d79428168f") (:authors ("Charles A. Roelli " . "charles@aurox.ch")) (:maintainers ("Charles A. Roelli " . "charles@aurox.ch")) (:maintainer "Charles A. Roelli " . "charles@aurox.ch") (:keywords "org" "bookmarks" "archives") (:url . "https://github.com/scallywag/org-board"))]) (org-bookmark-heading . [(20231216 1234) ((emacs (24 4))) "Emacs bookmark support for Org mode" tar ((:commit . "ed8b7fe2a08b06a1d750d1e1230e6728815e0bcd") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "hypermedia" "outlines") (:url . "http://github.com/alphapapa/org-bookmark-heading"))]) - (org-bookmarks . [(20240602 929) ((emacs (26 1))) "Manage bookmarks in Org mode" tar ((:commit . "e673af4511f33b0e673e584be9db7bfa5a7e5620") (:keywords "outline" "matching" "hypermedia" "org") (:url . "https://repo.or.cz/org-bookmarks.git"))]) + (org-bookmarks . [(20240606 758) ((emacs (26 1))) "Manage bookmarks in Org mode" tar ((:commit . "e96919be0199edf68eccac1dc92956fff1bc968c") (:keywords "outline" "matching" "hypermedia" "org") (:url . "https://repo.or.cz/org-bookmarks.git"))]) (org-bookmarks-extractor . [(20220829 146) ((emacs (25 1))) "Extract bookmarks from Org mode" tar ((:commit . "26d810d4d58de1f64f0bbd649e13816f96663d73") (:authors ("Xuqing Jia" . "jxq@jxq.me")) (:maintainers ("Xuqing Jia" . "jxq@jxq.me")) (:maintainer "Xuqing Jia" . "jxq@jxq.me") (:keywords "convenience" "org") (:url . "https://github.com/jxq0/org-bookmarks-extractor"))]) (org-books . [(20210408 1913) ((enlive (0 0 1)) (s (1 11 0)) (helm (2 9 2)) (helm-org (1 0)) (dash (2 14 1)) (org (9 3)) (emacs (25))) "Reading list management with Org mode and helm" tar ((:commit . "9f4ec4a981bfc5eebff993c3ad49a4bed26aebd1") (:authors ("Abhinav Tushar" . "abhinav@lepisma.xyz")) (:maintainers ("Abhinav Tushar" . "abhinav@lepisma.xyz")) (:maintainer "Abhinav Tushar" . "abhinav@lepisma.xyz") (:keywords "outlines") (:url . "https://github.com/lepisma/org-books"))]) (org-brain . [(20230217 1908) ((emacs (25 1)) (org (9 2))) "Org-mode concept mapping" tar ((:commit . "2bad7732aae1a3051e2a14de2e30f970bbe43c25") (:authors ("Erik Sjöstrand" . "sjostrand.erik@gmail.com")) (:maintainers ("Erik Sjöstrand" . "sjostrand.erik@gmail.com")) (:maintainer "Erik Sjöstrand" . "sjostrand.erik@gmail.com") (:keywords "outlines" "hypermedia") (:url . "http://github.com/Kungsgeten/org-brain"))]) @@ -3782,7 +3783,7 @@ (org-clock-split . [(20200331 526) ((emacs (24))) "Split clock entries" tar ((:commit . "39e1d2912a7a7223e2356a5fc4dff03507ae084d") (:authors ("Justin Taft <https://github.com/justintaft>")) (:maintainers ("Justin Taft <https://github.com/justintaft>")) (:maintainer "Justin Taft <https://github.com/justintaft>") (:keywords "calendar") (:url . "https://github.com/justintaft/emacs-org-clock-split"))]) (org-clock-today . [(20220918 514) ((emacs (25))) "Show total clocked time of the current day in the mode line" tar ((:commit . "b73cca120eb64538ab0666892a8b97b6d65b4d6b") (:authors ("Tijs Mallaerts" . "tijs.mallaerts@gmail.com")) (:maintainers ("Tijs Mallaerts" . "tijs.mallaerts@gmail.com")) (:maintainer "Tijs Mallaerts" . "tijs.mallaerts@gmail.com") (:url . "https://github.com/mallt/org-clock-today-mode"))]) (org-commentary . [(20160802 637) ((dash (2 0)) (emacs (24 4)) (org (8 0))) "generate or update conventional library headers using Org mode files" tar ((:commit . "821ccb994811359c42f4e3d459e0e88849d42b75") (:authors ("Sergei Maximov" . "s.b.maximov@gmail.com")) (:maintainers ("Sergei Maximov" . "s.b.maximov@gmail.com")) (:maintainer "Sergei Maximov" . "s.b.maximov@gmail.com") (:keywords "convenience" "docs" "tools") (:url . "https://github.com/smaximov/org-commentary"))]) - (org-contacts . [(20240521 1144) ((emacs (27 1)) (org (9 7))) "Contacts management system for Org mode" tar ((:commit . "722bc2ec91ca66a33f6b57cd02010f4fdbf542a9") (:authors ("Julien Danjou" . "julien@danjou.info")) (:maintainers ("stardiviner" . "numbchild@gmail.com")) (:maintainer "stardiviner" . "numbchild@gmail.com") (:keywords "contacts" "org-mode" "outlines" "hypermedia" "calendar") (:url . "https://repo.or.cz/org-contacts.git"))]) + (org-contacts . [(20240609 1058) ((emacs (27 1)) (org (9 7))) "Contacts management system for Org mode" tar ((:commit . "d0cb221502c9e104b6e3c358128b28761ffddb55") (:authors ("Julien Danjou" . "julien@danjou.info")) (:maintainers ("stardiviner" . "numbchild@gmail.com")) (:maintainer "stardiviner" . "numbchild@gmail.com") (:keywords "contacts" "org-mode" "outlines" "hypermedia" "calendar") (:url . "https://repo.or.cz/org-contacts.git"))]) (org-context . [(20220606 1339) nil "Contextual capture and agenda commands for Org-mode" tar ((:commit . "47bd45149cb74dab2ebecccfb918f6f8502a4f2c") (:authors ("Sylvain Rousseau <thisirs at gmail dot com>")) (:maintainers ("Sylvain Rousseau <thisirs at gmail dot com>")) (:maintainer "Sylvain Rousseau <thisirs at gmail dot com>") (:keywords "org" "capture" "agenda" "convenience") (:url . "https://github.com/thisirs/org-context"))]) (org-cua-dwim . [(20120203 534) nil "Org-mode and Cua mode compatibility layer" tar ((:commit . "a55d6c7009fc0b22f1110c07de629acc955c85e4") (:authors ("Matthew L. Fidler")) (:maintainers ("Matthew L. Fidler")) (:maintainer "Matthew L. Fidler") (:keywords "org-mode" "cua-mode"))]) (org-custom-cookies . [(20240414 44) ((emacs (25 1)) (org (9 4))) "Custom cookies for org-mode" tar ((:commit . "5650c73d20e53310dab62f6a65754a55aea9b40b") (:authors ("Gulshan Singh" . "gsingh2011@gmail.com")) (:maintainers ("Gulshan Singh" . "gsingh2011@gmail.com")) (:maintainer "Gulshan Singh" . "gsingh2011@gmail.com") (:url . "https://github.com/gsingh93/org-custom-cookies"))]) @@ -3828,11 +3829,11 @@ (org-journal . [(20240218 1645) ((emacs (26 1)) (org (9 1))) "a simple org-mode based journaling mode" tar ((:commit . "859dc19168dc6b10eba3843f24980a7db79f6869") (:authors ("Bastian Bechtold") ("Christian Schwarzgruber")) (:maintainers ("Bastian Bechtold")) (:maintainer "Bastian Bechtold") (:url . "http://github.com/bastibe/org-journal"))]) (org-journal-list . [(20190221 2052) ((emacs (25))) "Org mode Journal List" tar ((:commit . "2b26d00181bb49bff64b31ad020490acd1b6ae02") (:authors ("Huy Tran" . "huytd189@gmail.com")) (:maintainers ("Huy Tran" . "huytd189@gmail.com")) (:maintainer "Huy Tran" . "huytd189@gmail.com") (:url . "https://github.com/huytd/org-journal-list"))]) (org-journal-tags . [(20240101 4) ((emacs (27 1)) (org-journal (2 1 2)) (magit-section (3 3 0)) (transient (0 3 7))) "Tagging and querying system for org-journal" tar ((:commit . "a68e40a8473ff18bef58a171245a9cdef6eee622") (:authors ("Korytov Pavel" . "thexcloud@gmail.com")) (:maintainers ("Korytov Pavel" . "thexcloud@gmail.com")) (:maintainer "Korytov Pavel" . "thexcloud@gmail.com") (:url . "https://github.com/SqrtMinusOne/org-journal-tags"))]) - (org-kanban . [(20240601 2153) ((s (0)) (dash (2 17 0))) "kanban dynamic block for org-mode." tar ((:commit . "93ceed7e5d837c4cd7daa2ad36426669d38f4e46") (:authors ("Christian Köstlin" . "christian.koestlin@gmail.com")) (:maintainers ("Christian Köstlin" . "christian.koestlin@gmail.com")) (:maintainer "Christian Köstlin" . "christian.koestlin@gmail.com") (:keywords "org-mode" "org" "kanban" "tools") (:url . "http://github.com/gizmomogwai/org-kanban"))]) + (org-kanban . [(20240607 1612) ((s (0)) (dash (2 17 0))) "kanban dynamic block for org-mode." tar ((:commit . "cdc66ff97cdf5275db9f507bf2c915bbc0183c30") (:authors ("Christian Köstlin" . "christian.koestlin@gmail.com")) (:maintainers ("Christian Köstlin" . "christian.koestlin@gmail.com")) (:maintainer "Christian Köstlin" . "christian.koestlin@gmail.com") (:keywords "org-mode" "org" "kanban" "tools") (:url . "http://github.com/gizmomogwai/org-kanban"))]) (org-kindle . [(20220210 1408) ((emacs (25)) (cl-lib (0 5)) (seq (2 20))) "Send org link file to ebook reader." tar ((:commit . "fadcfd62e254d0c45e87d63128a82a08ae21869a") (:keywords "org" "link" "ebook" "kindle" "epub" "azw3" "mobi") (:url . "https://repo.or.cz/org-kindle.git"))]) (org-latex-impatient . [(20221111 623) ((emacs (26)) (s (1 8 0)) (posframe (0 8 0)) (org (9 3)) (dash (2 17 0))) "Preview org-latex Fragments Instantly via MathJax" tar ((:commit . "031025a8be9bf7255aa047388d027642cd2d6183") (:authors ("Sheng Yang" . "styang@fastmail.com")) (:maintainers ("Sheng Yang" . "styang@fastmail.com")) (:maintainer "Sheng Yang" . "styang@fastmail.com") (:keywords "tex" "tools") (:url . "https://github.com/yangsheng6810/org-latex-instant-preview"))]) (org-linenote . [(20240325 320) ((emacs (29 1)) (projectile (2 8 0)) (vertico (1 7))) "A package inspired by VSCode Linenote" tar ((:commit . "4c081f4bbe13c48df7cb17f2f006465b8b95196b") (:authors ("Jason Kim" . "sukbeom.kim@gmail.com")) (:maintainers ("Jason Kim" . "sukbeom.kim@gmail.com")) (:maintainer "Jason Kim" . "sukbeom.kim@gmail.com") (:keywords "tools" "note" "org") (:url . "https://github.com/seokbeomKim/org-linenote"))]) - (org-link-beautify . [(20240530 113) ((emacs (28 1)) (nerd-icons (0 0 1)) (fb2-reader (0 1 1)) (qrencode (1 2))) "Beautify Org Links" tar ((:commit . "d91f56ca7a4ad63100671a415b0f53d040ef89e8") (:keywords "hypermedia") (:url . "https://repo.or.cz/org-link-beautify.git"))]) + (org-link-beautify . [(20240605 326) ((emacs (28 1)) (nerd-icons (0 0 1)) (fb2-reader (0 1 1)) (qrencode (1 2))) "Beautify Org Links" tar ((:commit . "e2d1a32a62cbd87ca7abdbff408712e4396b02c8") (:keywords "hypermedia") (:url . "https://repo.or.cz/org-link-beautify.git"))]) (org-link-travis . [(20140405 2327) ((org (7))) "Insert/Export the link of Travis CI on org-mode" tar ((:commit . "596615ad8373d9090bd4138da683524f0ad0bda5") (:authors ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainers ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainer "Hiroaki Otsu" . "ootsuhiroaki@gmail.com") (:keywords "org") (:url . "https://github.com/aki2o/org-link-travis"))]) (org-linkotron . [(20200112 2235) ((emacs (26 1)) (org (9 3))) "Org-mode link selector" tar ((:commit . "d0adc5247b205bc73d2f1a83d4a512d2be541eb5") (:authors ("Per Weijnitz" . "per.weijnitz@gmail.com")) (:maintainers ("Per Weijnitz" . "per.weijnitz@gmail.com")) (:maintainer "Per Weijnitz" . "per.weijnitz@gmail.com") (:keywords "hypermedia" "org") (:url . "https://gitlab.com/perweij/org-linkotron"))]) (org-listcruncher . [(20210706 1741) ((seq (2 3)) (emacs (26 1))) "Planning tool - Parse Org mode lists into table" tar ((:commit . "075e0e6d36eb50406a608bc8a2f0dd359ec63938") (:authors ("Derek Feichtinger" . "dfeich@gmail.com")) (:maintainers ("Derek Feichtinger" . "dfeich@gmail.com")) (:maintainer "Derek Feichtinger" . "dfeich@gmail.com") (:keywords "convenience") (:url . "https://github.com/dfeich/org-listcruncher"))]) @@ -3850,7 +3851,7 @@ (org-multi-wiki . [(20210324 1820) ((emacs (26 1)) (dash (2 12)) (s (1 12)) (org-ql (0 5)) (org (9 3))) "Multiple wikis based on Org mode" tar ((:commit . "bf8039aadddaf02569fab473f766071ef7e63563") (:authors ("Akira Komamura" . "akira.komamura@gmail.com")) (:maintainers ("Akira Komamura" . "akira.komamura@gmail.com")) (:maintainer "Akira Komamura" . "akira.komamura@gmail.com") (:keywords "org" "outlines" "files") (:url . "https://github.com/akirak/org-multi-wiki"))]) (org-multiple-keymap . [(20191017 1920) ((org (8 2 4)) (emacs (24)) (cl-lib (0 5))) "Set keymap to elements, such as timestamp and priority." tar ((:commit . "4eb8aa0aada012b2346cc7f0c55e07783141a2c3") (:authors ("myuhe <yuhei.maeda_at_gmail.com>")) (:maintainers ("myuhe")) (:maintainer "myuhe") (:keywords "convenience" "org-mode") (:url . "https://github.com/myuhe/org-multiple-keymap.el"))]) (org-newtab . [(20240227 155) ((emacs (27 1)) (websocket (1 14)) (async (1 9 7))) "Supercharge your browser's new tab page" tar ((:commit . "eca494a43e242558bd8db24d321ad62a8ec86c02") (:authors ("Zweihänder" . "zweidev@zweihander.me")) (:maintainers ("Zweihänder" . "zweidev@zweihander.me")) (:maintainer "Zweihänder" . "zweidev@zweihander.me") (:keywords "outlines") (:url . "https://github.com/Zweihander-Main/org-newtab"))]) - (org-nix-shell . [(20240205 1642) ((emacs (27 1)) (org (9 4))) "Org local nix-shell" tar ((:commit . "d9843aa0f62a39b9938a89388e25129ecb39a4cc") (:maintainers ("Anton Hakansson" . "anton@hakanssn.com")) (:maintainer "Anton Hakansson" . "anton@hakanssn.com") (:keywords "processes" "outlines") (:url . "https://github.com/AntonHakansson/"))]) + (org-nix-shell . [(20240603 859) ((emacs (27 1)) (org (9 4))) "Org local nix-shell" tar ((:commit . "f359d9e1053fadee86dd668f4789ae2e700d8e8a") (:maintainers ("Anton Hakansson" . "anton@hakanssn.com")) (:maintainer "Anton Hakansson" . "anton@hakanssn.com") (:keywords "processes" "outlines") (:url . "https://github.com/AntonHakansson/"))]) (org-notebook . [(20170322 452) ((emacs (24)) (org (8)) (cl-lib (0 5))) "Ease the use of org-mode as a notebook" tar ((:commit . "d90c4aeca2442161e6dd89de175561af85aace03") (:authors ("Paul Elder" . "paul.elder@amanokami.net")) (:maintainers ("Paul Elder" . "paul.elder@amanokami.net")) (:maintainer "Paul Elder" . "paul.elder@amanokami.net") (:keywords "convenience" "tools"))]) (org-noter . [(20240509 1756) ((emacs (24 4)) (cl-lib (0 6)) (org (9 4))) "A synchronized, Org-mode, document annotator" tar ((:commit . "68646b685a0d8c02419234922a9e2d885d6419df") (:authors ("Gonçalo Santos (github.com/weirdNox)" . "in@bsentia") (" Maintainer Dmitry M" . "dmitrym@gmail.com")) (:maintainers ("Peter Mao" . "peter.mao@gmail.com")) (:maintainer "Peter Mao" . "peter.mao@gmail.com") (:keywords "lisp" "pdf" "interleave" "annotate" "external" "sync" "notes" "documents" "org-mode") (:url . "https://github.com/org-noter/org-noter"))]) (org-noter-pdftools . [(20230725 1433) ((emacs (26 1)) (org (9 4)) (pdf-tools (0 8)) (org-pdftools (1 0)) (org-noter (1 4 1))) "Integration between org-pdftools and org-noter" tar ((:commit . "4e420233a153a9c4ab3d1a7e1d7d3211c836f0ac") (:authors ("Alexander Fu Xi" . "fuxialexander@gmail.com")) (:maintainers ("Alexander Fu Xi" . "fuxialexnader@gmail.com")) (:maintainer "Alexander Fu Xi" . "fuxialexnader@gmail.com") (:keywords "convenience") (:url . "https://github.com/fuxialexander/org-pdftools"))]) @@ -3884,7 +3885,7 @@ (org-recent-headings . [(20211011 1519) ((emacs (26 1)) (org (9 0 5)) (dash (2 18 0)) (frecency (0 1)) (s (1 12 0))) "Jump to recently used Org headings" tar ((:commit . "97418d581ea030f0718794e50b005e9bae44582e") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "hypermedia" "outlines" "org") (:url . "http://github.com/alphapapa/org-recent-headings"))]) (org-recur . [(20230124 1532) ((emacs (24 1)) (org (9 0)) (dash (2 7 0))) "Recurring org-mode tasks" tar ((:commit . "628099883a63d219f76cd9631cc914fe6ec8a3e3") (:authors ("Marcin Swieczkowski" . "marcin.swieczkowski@gmail.com")) (:maintainers ("Marcin Swieczkowski" . "marcin.swieczkowski@gmail.com")) (:maintainer "Marcin Swieczkowski" . "marcin.swieczkowski@gmail.com") (:url . "https://github.com/mrcnski/org-recur"))]) (org-redmine . [(20160711 1114) nil "Redmine tools using Emacs OrgMode" tar ((:commit . "a526c3ac802634486bf10de9c2283ccb1a30ec8d") (:authors ("Wataru MIYAGUNI" . "gonngo@gmail.com")) (:maintainers ("Wataru MIYAGUNI" . "gonngo@gmail.com")) (:maintainer "Wataru MIYAGUNI" . "gonngo@gmail.com") (:keywords "redmine" "org") (:url . "https://github.com/gongo/org-redmine"))]) - (org-ref . [(20240602 1316) ((org (9 4)) (dash (0)) (s (0)) (f (0)) (htmlize (0)) (hydra (0)) (avy (0)) (parsebib (0)) (bibtex-completion (0)) (citeproc (0)) (ox-pandoc (0)) (request (0))) "citations, cross-references and bibliographies in org-mode" tar ((:commit . "23d2e06b144e464e3c9498522e090813742b7b7c") (:authors ("John Kitchin" . "jkitchin@andrew.cmu.edu")) (:maintainers ("John Kitchin" . "jkitchin@andrew.cmu.edu")) (:maintainer "John Kitchin" . "jkitchin@andrew.cmu.edu") (:keywords "org-mode" "cite" "ref" "label") (:url . "https://github.com/jkitchin/org-ref"))]) + (org-ref . [(20240602 2029) ((org (9 4)) (dash (0)) (s (0)) (f (0)) (htmlize (0)) (hydra (0)) (avy (0)) (parsebib (0)) (bibtex-completion (0)) (citeproc (0)) (ox-pandoc (0)) (request (0))) "citations, cross-references and bibliographies in org-mode" tar ((:commit . "cac1acb2501862bbcebb3d3ffb3691be5c44eb62") (:authors ("John Kitchin" . "jkitchin@andrew.cmu.edu")) (:maintainers ("John Kitchin" . "jkitchin@andrew.cmu.edu")) (:maintainer "John Kitchin" . "jkitchin@andrew.cmu.edu") (:keywords "org-mode" "cite" "ref" "label") (:url . "https://github.com/jkitchin/org-ref"))]) (org-ref-prettify . [(20220507 649) ((emacs (24 3)) (org-ref (3 0)) (bibtex-completion (1 0 0))) "Prettify org-ref citation links" tar ((:commit . "0ec3b6e398ee117c8b8a787a0422b95d9e95f7bb") (:authors ("Alex Kost" . "alezost@gmail.com") ("Vitus Schäfftlein" . "vitusschaefftlein@live.de")) (:maintainers ("Alex Kost" . "alezost@gmail.com")) (:maintainer "Alex Kost" . "alezost@gmail.com") (:keywords "convenience") (:url . "https://github.com/alezost/org-ref-prettify.el"))]) (org-repo-todo . [(20171228 119) nil "Simple repository todo management with org-mode" tar ((:commit . "f73ebd91399c5760ad52c6ad9033de1066042003") (:authors ("justin talbott" . "justin@waymondo.com")) (:maintainers ("justin talbott" . "justin@waymondo.com")) (:maintainer "justin talbott" . "justin@waymondo.com") (:keywords "convenience") (:url . "https://github.com/waymondo/org-repo-todo"))]) (org-reverse-datetree . [(20240530 1306) ((emacs (29 1)) (dash (2 19 1)) (org (9 6))) "Create reverse date trees in org-mode" tar ((:commit . "116d99a6bdf873cb61cc508afb48facffda0c049") (:authors ("Akira Komamura" . "akira.komamura@gmail.com")) (:maintainers ("Akira Komamura" . "akira.komamura@gmail.com")) (:maintainer "Akira Komamura" . "akira.komamura@gmail.com") (:keywords "outlines") (:url . "https://github.com/akirak/org-reverse-datetree"))]) @@ -3901,7 +3902,7 @@ (org-runbook . [(20230503 319) ((emacs (27 1)) (seq (2 3)) (f (0 20 0)) (s (1 12 0)) (dash (2 17 0)) (mustache (0 24)) (ht (0 9)) (ivy (0 8 0))) "Org mode for runbooks" tar ((:commit . "7ada3903a56266d60541d59ae92410e8ab6fe836") (:authors ("Tyler Dodge")) (:maintainers ("Tyler Dodge")) (:maintainer "Tyler Dodge") (:keywords "convenience" "processes" "terminals" "files") (:url . "https://github.com/tyler-dodge/org-runbook"))]) (org-scrum . [(20200131 1129) ((emacs (24 5)) (org (8 2)) (seq (2 3)) (cl-lib (1 0))) "org mode extensions for scrum planning and reporting" tar ((:commit . "a383348ea80c2459bfb96fa0652b98f0059bd311") (:authors ("Ian Martins" . "ianxm@jhu.edu")) (:maintainers ("Ian Martins" . "ianxm@jhu.edu")) (:maintainer "Ian Martins" . "ianxm@jhu.edu") (:url . "https://github.com/ianxm/emacs-scrum"))]) (org-seek . [(20161217 502) ((emacs (24 3)) (ag (0 48))) "Searching Org-mode files with search tools." tar ((:commit . "1f51e6634e3b9a6a29d335d0d14370a6ffef2265") (:authors ("stardiviner" . "numbchild@gmail.com")) (:maintainers ("stardiviner" . "numbchild@gmail.com")) (:maintainer "stardiviner" . "numbchild@gmail.com") (:keywords "org" "search" "ag" "pt") (:url . "https://github.com/stardiviner/org-seek.el"))]) - (org-shoplist . [(20210629 2157) ((emacs (25))) "Eat the world" tar ((:commit . "71ea7643e66c97d21df49fb8b600578ca0464f83") (:authors ("lordnik22")) (:maintainers ("lordnik22")) (:maintainer "lordnik22") (:keywords "extensions" "matching") (:url . "https://github.com/lordnik22"))]) + (org-shoplist . [(20240605 2257) ((emacs (25))) "Eat the world" tar ((:commit . "6c2daa0b663d01a498dca2f2f4f4b645be1c365a") (:authors ("lordnik22")) (:maintainers ("lordnik22")) (:maintainer "lordnik22") (:keywords "extensions" "matching") (:url . "https://github.com/lordnik22"))]) (org-side-tree . [(20240601 1001) ((emacs (28 1))) "Navigate Org outlines in side window tree" tar ((:commit . "e8da5217ce23440a62f4a46ef60e2082b6284b28") (:authors ("Grant Rosson <https://github.com/localauthor>")) (:maintainers ("Grant Rosson <https://github.com/localauthor>")) (:maintainer "Grant Rosson <https://github.com/localauthor>") (:url . "https://github.com/localauthor/org-side-tree"))]) (org-sidebar . [(20240102 9) ((emacs (26 1)) (compat (29 1)) (s (1 10 0)) (dash (2 18)) (org (9 6)) (org-ql (0 2)) (org-super-agenda (1 0))) "Helpful sidebar for Org buffers" tar ((:commit . "1e06d1b4ab5f0d09301712cdecb757c9437a7179") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "hypermedia" "outlines" "org" "agenda") (:url . "https://github.com/alphapapa/org-sidebar"))]) (org-sliced-images . [(20240325 710) ((emacs (29 1)) (org (9 6 15))) "Sliced inline images in org-mode" tar ((:commit . "f3964d4ba421953fe9f109a99811b6d884ca56ab") (:authors ("Jacob Fong" . "jacobcfong@gmail.com")) (:maintainers ("Jacob Fong" . "jacobcfong@gmail.com")) (:maintainer "Jacob Fong" . "jacobcfong@gmail.com") (:url . "https://github.com/jcfk/org-sliced-images"))]) @@ -3919,7 +3920,7 @@ (org-table-color . [(20220311 1927) ((emacs (26 1))) "Add color to your org-mode table cells" tar ((:commit . "2022f301ef323953c3a0e087a1b601da85e06da1") (:authors ("Colin Woodbury" . "colin@fosskers.ca")) (:maintainers ("Colin Woodbury" . "colin@fosskers.ca")) (:maintainer "Colin Woodbury" . "colin@fosskers.ca") (:keywords "data" "faces" "lisp") (:url . "https://github.com/fosskers/org-table-color"))]) (org-table-comment . [(20120209 1851) nil "Org table comment modes." tar ((:commit . "33b9966c33ecbc3e27cca67c2f2cdea04364d74e") (:authors ("Matthew L. Fidler <matthew dot fidler at gmail . com>")) (:maintainers ("Matthew L. Fidler")) (:maintainer "Matthew L. Fidler") (:keywords "org-mode" "orgtbl") (:url . "http://github.com/mlf176f2/org-table-comment.el"))]) (org-table-sticky-header . [(20190924 506) ((org (8 2 10)) (emacs (24 4))) "Sticky header for org-mode tables" tar ((:commit . "b65442857128ab04724aaa301e60aa874a31a798") (:authors ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainers ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainer "Junpeng Qiu" . "qjpchmail@gmail.com") (:keywords "extensions"))]) - (org-tag-beautify . [(20240531 1220) ((emacs (26 1)) (nerd-icons (0 0 1))) "Beautify Org mode tags" tar ((:commit . "7a20f8ea4143a9182cc78cb5a6b452ad40a22836") (:keywords "hypermedia") (:url . "https://repo.or.cz/org-tag-beautify.git"))]) + (org-tag-beautify . [(20240609 1059) ((emacs (26 1)) (nerd-icons (0 0 1))) "Beautify Org mode tags" tar ((:commit . "413b39718af30b4dc7cbd9fc56ca929ae85fd11d") (:keywords "hypermedia") (:url . "https://repo.or.cz/org-tag-beautify.git"))]) (org-tagged . [(20220926 2048) ((s (1 13 0)) (dash (2 19 1)) (emacs (28 1)) (org (9 5 2))) "Dynamic block for tagged org-mode todos" tar ((:commit . "4b0174473772fca976426e982bb3f4a3037c1e37") (:authors ("Christian Köstlin" . "christian.koestlin@gmail.com")) (:maintainers ("Christian Köstlin" . "christian.koestlin@gmail.com")) (:maintainer "Christian Köstlin" . "christian.koestlin@gmail.com") (:keywords "org-mode" "org" "gtd" "tools") (:url . "http://github.com/gizmomogwai/org-tagged"))]) (org-tanglesync . [(20200127 1616) ((emacs (24 4))) "Syncing org src blocks with tangled external files" tar ((:commit . "31aa5502d1d4f8b032807949908c016b00556684") (:authors ("Mehmet Tekman")) (:maintainers ("Mehmet Tekman")) (:maintainer "Mehmet Tekman") (:keywords "outlines") (:url . "https://github.com/mtekman/org-tanglesync.el"))]) (org-tfl . [(20170923 1218) ((org (0 16 2)) (cl-lib (0 5)) (emacs (24 1))) "Transport for London meets Orgmode" tar ((:commit . "f0d7d39106a1de5457f5160cddd98ab892b61066") (:authors ("storax (David Zuber), <zuber [dot] david [at] gmx [dot] de>")) (:maintainers ("storax (David Zuber), <zuber [dot] david [at] gmx [dot] de>")) (:maintainer "storax (David Zuber), <zuber [dot] david [at] gmx [dot] de>") (:keywords "org" "tfl") (:url . "https://github.com/storax/org-tfl"))]) @@ -3943,7 +3944,7 @@ (org-visibility . [(20220929 1415) ((emacs (27 1))) "Persistent org tree visibility" tar ((:commit . "afa4b6f8ff274df87eb11f1afd0321084a45a2ab") (:authors ("Kyle W T Sherman" . "kylewsherman@gmail.com")) (:maintainers ("Kyle W T Sherman" . "kylewsherman@gmail.com")) (:maintainer "Kyle W T Sherman" . "kylewsherman@gmail.com") (:keywords "outlines" "convenience") (:url . "https://github.com/nullman/emacs-org-visibility"))]) (org-wc . [(20200731 2244) nil "Count words in org mode trees." tar ((:commit . "dbbf794e4ec6c4080d945f43338185e34a4a582d") (:authors ("Simon Guest")) (:maintainers ("Simon Guest")) (:maintainer "Simon Guest"))]) (org-web-tools . [(20231220 1515) ((emacs (27 1)) (org (9 0)) (compat (29 1 4 2)) (dash (2 12)) (esxml (0 3 4)) (s (1 10 0)) (plz (0 7 1)) (request (0 3 0))) "Display and capture web content with Org-mode" tar ((:commit . "7a6498f442fc7f29504745649948635c7165d847") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "hypermedia" "outlines" "org" "web") (:url . "http://github.com/alphapapa/org-web-tools"))]) - (org-web-track . [(20240424 756) ((emacs (29 1)) (request (0 3 0)) (enlive (0 0 1))) "Web data tracking framework in org mode" tar ((:commit . "92e97112aa63cbe14f7727b157eb3c11239bf86c") (:authors ("p-snow" . "public@p-snow.org")) (:maintainers ("p-snow" . "public@p-snow.org")) (:maintainer "p-snow" . "public@p-snow.org") (:keywords "org" "agenda" "web" "hypermedia") (:url . "https://github.com/p-snow/org-web-track"))]) + (org-web-track . [(20240609 1107) ((emacs (29 1)) (request (0 3 0)) (enlive (0 0 1))) "Web data tracking framework in Org Mode" tar ((:commit . "ee15faf47c308f37bfcfffe38e8f4ee55661af3e") (:authors ("p-snow" . "public@p-snow.org")) (:maintainers ("p-snow" . "public@p-snow.org")) (:maintainer "p-snow" . "public@p-snow.org") (:keywords "org" "agenda" "web" "hypermedia") (:url . "https://github.com/p-snow/org-web-track"))]) (org-wild-notifier . [(20240325 744) ((alert (1 2)) (async (1 9 3)) (dash (2 18 0)) (emacs (26 1))) "Customizable org-agenda notifications" tar ((:commit . "4c1679c12ebe0e4a97494d0673a5484f9e4d0ba6") (:authors ("Artem Khramov" . "akhramov+emacs@pm.me")) (:maintainers ("Artem Khramov" . "akhramov+emacs@pm.me")) (:maintainer "Artem Khramov" . "akhramov+emacs@pm.me") (:keywords "notification" "alert" "org" "org-agenda" "agenda") (:url . "https://github.com/akhramov/org-wild-notifier.el"))]) (org-working-set . [(20230803 1640) ((org (9 3)) (dash (2 12)) (s (1 12)) (emacs (26 3))) "Manage and visit a small and changing set of org-nodes that you work on" tar ((:commit . "c83a63f34829dca137941bc06e29c34bf056a43b") (:authors ("Marc Ihm" . "marc@ihm.name")) (:maintainers ("Marc Ihm" . "marc@ihm.name")) (:maintainer "Marc Ihm" . "marc@ihm.name") (:url . "https://github.com/marcIhm/org-working-set"))]) (org-wunderlist . [(20191017 1917) ((request-deferred (0 2 0)) (alert (1 1)) (emacs (24)) (cl-lib (0 5)) (org (8 2 4)) (s (1 9 0))) "Org sync with Wunderlist" tar ((:commit . "1a084bb49be4b5a1066db9cd9b7da2f8efab293f") (:authors ("myuhe <yuhei.maeda_at_gmail.com>")) (:maintainers ("myuhe")) (:maintainer "myuhe") (:keywords "convenience") (:url . "https://github.com/myuhe/org-wunderlist.el"))]) @@ -4155,9 +4156,9 @@ (perspective-project-bridge . [(20231024 1737) ((emacs (27 1)) (perspective (2 18))) "Integration of perspective.el + project.el" tar ((:commit . "7b65b08a0151b8279fc3ae75f0016cb8d5eadb53") (:authors ("Arunkumar Vaidyanathan" . "arunkumarmv1997@gmail.com")) (:maintainers ("Arunkumar Vaidyanathan" . "arunkumarmv1997@gmail.com")) (:maintainer "Arunkumar Vaidyanathan" . "arunkumarmv1997@gmail.com") (:keywords "perspective" "project" "convenience" "frames") (:url . "https://github.com/arunkmv/perspective-project-bridge"))]) (perspeen . [(20171203 1021) ((emacs (25 0)) (powerline (2 4))) "An package for multi-workspace" tar ((:commit . "edb70c530bda50ff3d1756e32a703d5fef5e5480") (:authors ("Peng Li" . "seudut@gmail.com")) (:maintainers ("Peng Li" . "seudut@gmail.com")) (:maintainer "Peng Li" . "seudut@gmail.com") (:keywords "lisp") (:url . "https://github.com/seudut/perspeen"))]) (pest-mode . [(20221231 15) ((emacs (26 3))) "Major mode for editing Pest files" tar ((:commit . "8023a92ce59c34dcd1587cbd85ed144f206ddb89") (:authors ("ksqsf" . "i@ksqsf.moe")) (:maintainers ("ksqsf" . "i@ksqsf.moe")) (:maintainer "ksqsf" . "i@ksqsf.moe") (:keywords "languages") (:url . "https://github.com/ksqsf/pest-mode"))]) - (pet . [(20240601 2355) ((emacs (26 1)) (f (0 6 0)) (map (3 3 1)) (seq (2 24))) "Executable and virtualenv tracker for python-mode" tar ((:commit . "44304055d666176bf765595f294a39b3226033bd") (:authors ("Jimmy Yuen Ho Wong" . "wyuenho@gmail.com")) (:maintainers ("Jimmy Yuen Ho Wong" . "wyuenho@gmail.com")) (:maintainer "Jimmy Yuen Ho Wong" . "wyuenho@gmail.com") (:keywords "tools") (:url . "https://github.com/wyuenho/emacs-pet/"))]) + (pet . [(20240608 2012) ((emacs (26 1)) (f (0 6 0)) (map (3 3 1)) (seq (2 24))) "Executable and virtualenv tracker for python-mode" tar ((:commit . "c23dea50711bb906bc0d972e3a49af008310e3a2") (:authors ("Jimmy Yuen Ho Wong" . "wyuenho@gmail.com")) (:maintainers ("Jimmy Yuen Ho Wong" . "wyuenho@gmail.com")) (:maintainer "Jimmy Yuen Ho Wong" . "wyuenho@gmail.com") (:keywords "tools") (:url . "https://github.com/wyuenho/emacs-pet/"))]) (pfuture . [(20220913 1401) ((emacs (25 2))) "a simple wrapper around asynchronous processes" tar ((:commit . "19b53aebbc0f2da31de6326c495038901bffb73c") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainers ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/pfuture"))]) - (pg . [(20240520 1322) ((emacs (28 1)) (peg (1 0))) "Emacs Lisp socket-level interface to the PostgreSQL RDBMS" tar ((:commit . "0f20a0051ab898ea53903284c47a4703555380ca") (:authors ("Eric Marsden" . "eric.marsden@risk-engineering.org")) (:maintainers ("Eric Marsden" . "eric.marsden@risk-engineering.org")) (:maintainer "Eric Marsden" . "eric.marsden@risk-engineering.org") (:keywords "data" "comm" "database" "postgresql") (:url . "https://github.com/emarsden/pg-el"))]) + (pg . [(20240608 1424) ((emacs (28 1)) (peg (1 0))) "Emacs Lisp socket-level interface to the PostgreSQL RDBMS" tar ((:commit . "ff3fed60447e8f6042acc275aa03c2a31327fddd") (:authors ("Eric Marsden" . "eric.marsden@risk-engineering.org")) (:maintainers ("Eric Marsden" . "eric.marsden@risk-engineering.org")) (:maintainer "Eric Marsden" . "eric.marsden@risk-engineering.org") (:keywords "data" "comm" "database" "postgresql") (:url . "https://github.com/emarsden/pg-el"))]) (pgdevenv . [(20150105 2236) nil "Manage your PostgreSQL development envs" tar ((:commit . "7f1d5bc734750aca98cf67a9491cdbd5615fd132") (:authors ("Dimitri Fontaine" . "dim@tapoueh.org")) (:maintainers ("Dimitri Fontaine" . "dim@tapoueh.org")) (:maintainer "Dimitri Fontaine" . "dim@tapoueh.org") (:keywords "emacs" "postgresql" "development" "environment" "shell" "debug" "gdb"))]) (ph . [(20161029 1522) ((emacs (24 3))) "A global minor mode for managing multiple projects." tar ((:commit . "a66e38637d1898b2ec31ee611033ac3f295fd97f") (:authors ("Alexander Gromnitsky" . "alexander.gromnitsky@gmail.com")) (:maintainer "Alexander Gromnitsky" . "alexander.gromnitsky@gmail.com"))]) (phabricator . [(20160510 1425) ((emacs (24 4)) (dash (1 0)) (projectile (0 13 0)) (s (1 10 0)) (f (0 17 2))) "Phabricator/Arcanist helpers for Emacs." tar ((:commit . "d09d6f059aea92d3b11c68664a5e80c901182ab8") (:authors ("Andrew Tulloch")) (:maintainers ("Andrew Tulloch")) (:maintainer "Andrew Tulloch") (:keywords "phabricator" "arcanist" "diffusion") (:url . "https://github.com/ajtulloch/phabricator.el"))]) @@ -4235,7 +4236,7 @@ (pnpm-mode . [(20200527 557) ((emacs (24 1))) "Minor mode for working with pnpm projects" tar ((:commit . "ec66ba36ba6e07883b029569c33fd461d28eed75") (:authors ("Rajasegar Chandran" . "rajasegar.c@gmail.com")) (:maintainers ("Rajasegar Chandran" . "rajasegar.c@gmail.com")) (:maintainer "Rajasegar Chandran" . "rajasegar.c@gmail.com") (:keywords "convenience" "project" "javascript" "node" "npm" "pnpm") (:url . "https://github.com/rajasegar/pnpm-mode"))]) (po-mode . [(20231006 1425) nil "major mode for GNU gettext PO files" tar ((:commit . "ca125eba813a6b29b5fbe7ea8a2e3d92f225ab8c") (:keywords "i18n" "gettext"))]) (pocket-api . [(20180403 109) ((emacs (24 4)) (request (0 2))) "another pocket api" tar ((:commit . "3eb9430b9db90bc02e736e433eb86389f7655189") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainers ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "convenience" "pocket") (:url . "https://github.com/lujun9972/pocket-api.el"))]) - (pocket-lib . [(20240407 2247) ((emacs (25 1)) (plz (0 7 3)) (dash (2 13 0)) (kv (0 0 19)) (s (1 12 0))) "Library for accessing getpocket.com API" tar ((:commit . "b13c899223a15481738c3105f07ee2205dcc937c") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "pocket") (:url . "https://github.com/alphapapa/pocket-lib.el"))]) + (pocket-lib . [(20240607 523) ((emacs (25 1)) (plz (0 7 3)) (dash (2 13 0)) (kv (0 0 19)) (s (1 12 0))) "Library for accessing getpocket.com API" tar ((:commit . "124e1081ab432baf455534e7db713970d05e1144") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "pocket") (:url . "https://github.com/alphapapa/pocket-lib.el"))]) (pocket-mode . [(20171201 1315) ((emacs (24 4)) (pocket-api (0 1))) "Manage your pocket" tar ((:commit . "229de7d35b7e5605797591c46aa8200d7efc363c") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainers ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "convenience" "pocket"))]) (pocket-reader . [(20240407 2303) ((emacs (25 1)) (dash (2 13 0)) (kv (0 0 19)) (peg (1 0 1)) (pocket-lib (0 3 -1)) (s (1 10)) (ov (1 0 6)) (org-web-tools (0 1)) (ht (2 2))) "Client for Pocket reading list" tar ((:commit . "cb9f6b108ebd3a67f77fb75d85351ffb3b0bb3d4") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "pocket") (:url . "https://github.com/alphapapa/pocket-reader.el"))]) (podcaster . [(20200607 1054) ((cl-lib (0 5))) "Podcast client" tar ((:commit . "7a21173da0c57e6aa41dbdc33383047386b35eb5") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainers ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:url . "https://github.com/lujun9972/podcaster"))]) @@ -4307,7 +4308,7 @@ (pretty-speedbar . [(20220303 1726) ((emacs (27 1))) "Make speedbar pretty" tar ((:commit . "56dc9f114fcc55843e182cde1fc9d7a14c261c6a") (:authors ("Kristle Chester" . "kcyarn7@gmail.com")) (:maintainers ("Kristle Chester" . "kcyarn7@gmail.com")) (:maintainer "Kristle Chester" . "kcyarn7@gmail.com") (:keywords "file" "tags" "tools") (:url . "https://github.com/kcyarn/pretty-speedbar"))]) (pretty-symbols . [(20140814 959) nil "Draw tokens as Unicode glyphs." tar ((:commit . "ab82b3fba129fae14e4031eb7fd648c1a92d0e71") (:authors ("David Röthlisberger" . "david@rothlis.net")) (:maintainers ("David Röthlisberger" . "david@rothlis.net")) (:maintainer "David Röthlisberger" . "david@rothlis.net") (:keywords "faces") (:url . "http://github.com/drothlis/pretty-symbols"))]) (preview-dvisvgm . [(20211225 635) ((emacs (27 1)) (auctex (13 0 12))) "SVG output for LaTeX preview" tar ((:commit . "630e2f008c4a6c67a01824b7ad6b844977b28f87") (:authors ("Tobias Zawada" . "i@tn-home.de")) (:maintainers ("Tobias Zawada" . "i@tn-home.de")) (:maintainer "Tobias Zawada" . "i@tn-home.de") (:keywords "tex") (:url . "https://github.com/TobiasZawada/preview-dvisvgm"))]) - (prism . [(20230524 1130) ((emacs (26 1)) (dash (2 14 1))) "Customizable, depth-based syntax coloring" tar ((:commit . "169b49afa91e69d35b8756df49ed3ca06f418d35") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "faces" "lisp") (:url . "https://github.com/alphapapa/prism.el"))]) + (prism . [(20240605 1547) ((emacs (27 1)) (compat (29 1 4 5)) (dash (2 14 1))) "Customizable, depth-based syntax coloring" tar ((:commit . "1dad1d85e7a35e00abcddde22a192521f60cfdfc") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "faces" "lisp") (:url . "https://github.com/alphapapa/prism.el"))]) (prisma-ts-mode . [(20231022 1802) ((emacs (29 1))) "Major mode for prisma using tree-sitter" tar ((:commit . "a7029980140ae60612ef876efa17ab81bf4b3add") (:authors ("Noah Peart" . "noah.v.peart@gmail.com")) (:maintainers ("Noah Peart" . "noah.v.peart@gmail.com")) (:maintainer "Noah Peart" . "noah.v.peart@gmail.com") (:keywords "prisma" "languages" "tree-sitter") (:url . "https://github.com/nverno/prisma-ts-mode"))]) (private . [(20150122 157) ((aes (0 6))) "take care of your private configuration files." tar ((:commit . "f57f1c2f6bfe900bd40b252688df4c6ed6a5f44b") (:authors ("Cheung Mou Wai" . "yeannylam@gmail.com")) (:maintainers ("Cheung Mou Wai" . "yeannylam@gmail.com")) (:maintainer "Cheung Mou Wai" . "yeannylam@gmail.com") (:keywords "private" "configuration" "backup" "recover") (:url . "https://github.com/cheunghy/private"))]) (private-comments-mode . [(20220929 1807) ((emacs (27 1))) "Minor mode for masukomi/private_comments" tar ((:commit . "b32b862e42e1f5cf26b6ca4cebea69b3f4e1aeab") (:keywords "tools") (:url . "https://github.com/masukomi/private-comments-mode"))]) @@ -4459,7 +4460,7 @@ (quiz . [(20190525 1206) ((cl-lib (0 5)) (emacs (25))) "Multiple choice quiz game" tar ((:commit . "570bf53926d89282cdb9653bd5aa8fe968f92bbd") (:authors ("Dave Pearson" . "davep@davep.org")) (:maintainers ("Dave Pearson" . "davep@davep.org")) (:maintainer "Dave Pearson" . "davep@davep.org") (:keywords "games" "trivia" "quiz") (:url . "https://github.com/davep/quiz.el"))]) (r-autoyas . [(20140101 1510) ((ess (0)) (yasnippet (0 8 0))) "Provides automatically created yasnippets for R function argument lists." tar ((:commit . "d321a7da0ef2e94668d53e0807277da7b70ea678") (:authors ("Sven Hartenstein & Matthew Fidler")) (:maintainers ("Matthew Fidler")) (:maintainer "Matthew Fidler") (:keywords "r" "yasnippet") (:url . "https://github.com/mlf176f2/r-autoyas.el"))]) (racer . [(20210307 243) ((emacs (25 1)) (rust-mode (0 2 0)) (dash (2 13 0)) (s (1 10 0)) (f (0 18 2)) (pos-tip (0 4 6))) "code completion, goto-definition and docs browsing for Rust via racer" tar ((:commit . "1e63e98626737ea9b662d4a9b1ffd6842b1c648c") (:authors ("Phil Dawes")) (:maintainers ("Phil Dawes")) (:maintainer "Phil Dawes") (:keywords "abbrev" "convenience" "matching" "rust" "tools") (:url . "https://github.com/racer-rust/emacs-racer"))]) - (racket-mode . [(20240514 1524) ((emacs (25 1))) "Racket editing, REPL, and more" tar ((:commit . "d2cff2b7f210846108875c7eccb1b5ab98bceb74") (:authors ("Greg Hendershott" . "racket-mode-author@greghendershott.com")) (:maintainers ("Greg Hendershott")) (:maintainer "Greg Hendershott") (:url . "https://www.racket-mode.com/"))]) + (racket-mode . [(20240607 1211) ((emacs (25 1))) "Racket editing, REPL, and more" tar ((:commit . "ac5ede46b55b7602548c895272c8ab9c82053b2f") (:authors ("Greg Hendershott" . "racket-mode-author@greghendershott.com")) (:maintainers ("Greg Hendershott")) (:maintainer "Greg Hendershott") (:url . "https://www.racket-mode.com/"))]) (rails-i18n . [(20220126 1643) ((emacs (27 2)) (yaml (0 1 0)) (dash (2 19 1))) "Seach and insert i18n on ruby code" tar ((:commit . "8e87e4e48e31902b8259ded28a208c2e7efea6e9") (:authors ("Otávio Schwanck dos Santos" . "otavioschwanck@gmail.com")) (:maintainers ("Otávio Schwanck dos Santos" . "otavioschwanck@gmail.com")) (:maintainer "Otávio Schwanck dos Santos" . "otavioschwanck@gmail.com") (:keywords "tools" "languages") (:url . "https://github.com/otavioschwanck/rails-i18n.el"))]) (rails-log-mode . [(20140408 425) nil "Major mode for viewing Rails log files" tar ((:commit . "ff440003ad7d47cb0ac3300f2a632f4cfd36a446") (:authors ("Anantha kumaran" . "ananthakumaran@gmail.com")) (:maintainers ("Anantha kumaran" . "ananthakumaran@gmail.com")) (:maintainer "Anantha kumaran" . "ananthakumaran@gmail.com") (:keywords "rails" "log"))]) (rails-routes . [(20220126 1631) ((emacs (27 2)) (inflections (1 1))) "Search for and insert rails routes" tar ((:commit . "eab995a9297ca5bd9bd4f4c2737f2fecfc36def0") (:authors ("Otávio Schwanck" . "otavioschwanck@gmail.com")) (:maintainers ("Otávio Schwanck" . "otavioschwanck@gmail.com")) (:maintainer "Otávio Schwanck" . "otavioschwanck@gmail.com") (:keywords "tools" "languages") (:url . "https://github.com/otavioschwanck/rails-routes"))]) @@ -4727,7 +4728,7 @@ (semantic-thrift . [(20240415 1206) ((thrift (0 0 1)) (emacs (25 1))) "Thrift LALR parser" tar ((:commit . "a2ff23acc72f7955a485e08e2819d4892d5e6dca") (:authors (nil . "Guanghui Xu gh_xu@qq.com")) (:maintainers (nil . "Guanghui Xu gh_xu@qq.com")) (:maintainer nil . "Guanghui Xu gh_xu@qq.com") (:keywords "extensions" "thrift" "semantic") (:url . "https://github.com/jerryxgh/semantic-thrift"))]) (semaphore . [(20190607 1949) ((emacs (26))) "Semaphore based on condition variables" tar ((:commit . "ec4c485c8e4cff63805ecc25523a031a6c2ad7cd") (:authors ("Herwig Hochleitner" . "herwig@bendlas.net")) (:maintainers ("Herwig Hochleitner" . "herwig@bendlas.net")) (:maintainer "Herwig Hochleitner" . "herwig@bendlas.net") (:keywords "processes" "unix") (:url . "http://github.com/webnf/semaphore.el"))]) (semaphore-promise . [(20190607 2115) ((emacs (26)) (semaphore (1)) (promise (1))) "semaphore integration with promise" tar ((:commit . "9cdfef91cc0293371af549ad41027aa5b73f30a4") (:authors ("Herwig Hochleitner" . "herwig@bendlas.net")) (:maintainers ("Herwig Hochleitner" . "herwig@bendlas.net")) (:maintainer "Herwig Hochleitner" . "herwig@bendlas.net") (:keywords "processes" "unix") (:url . "http://github.com/webnf/semaphore.el"))]) - (semi . [(20231102 1035) ((emacs (24 5)) (apel (10 8)) (flim (1 14 9))) "A library to provide MIME features." tar ((:commit . "9063a4485b148a767ea924f0e7cc78d3524ba256"))]) + (semi . [(20240606 1327) ((emacs (24 5)) (apel (10 8)) (flim (1 14 9))) "A library to provide MIME features." tar ((:commit . "85a52b899ac89be504d9e38d8d406bba98f4b0b3"))]) (seml-mode . [(20230702 1446) ((emacs (25 1)) (impatient-mode (1 1)) (htmlize (1 5)) (web-mode (16 0))) "Major-mode for SEML, S-Expression Markup Language, file" tar ((:commit . "23d684ac590fad6aa3c5ce3962c4683c1eb8fdb5") (:authors ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainers ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "Naoya Yamashita" . "conao3@gmail.com") (:keywords "lisp" "html") (:url . "https://github.com/conao3/seml-mode.el"))]) (sendto . [(20160425 1250) ((emacs (24 4))) "send the region content to a function" tar ((:commit . "076b81d7a53f75b0a59b0ef3448f35570567054c") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainers ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "convenience" "region") (:url . "https://github.com/lujun9972/sendto.el"))]) (sensei . [(20220530 1226) ((emacs (27 1)) (projectile (2 5 0)) (request (0 3 2))) "A client for sensei" tar ((:commit . "3538990de9ab57154e3da08d10fbd2c6228d87b8") (:authors ("Arnaud Bailly" . "arnaud@pankzsoft.com")) (:maintainers ("Arnaud Bailly" . "arnaud@pankzsoft.com")) (:maintainer "Arnaud Bailly" . "arnaud@pankzsoft.com") (:keywords "hypermedia") (:url . "https://abailly.github.io/sensei"))]) @@ -4809,7 +4810,7 @@ (silkworm-theme . [(20210215 1120) ((emacs (24))) "Light theme with pleasant, low contrast colors." tar ((:commit . "ff80e9294da0fb093e15097ac62153ef4a64a889") (:authors ("Martin Haesler")) (:maintainers ("Martin Haesler")) (:maintainer "Martin Haesler"))]) (simp . [(20180607 254) nil "Simple project definition, chiefly for file finding, and grepping" tar ((:commit . "d4d4b8547055347828bedccbeffdb4fd2d5a5d34") (:authors ("atom smith")) (:maintainers ("atom smith")) (:maintainer "atom smith") (:keywords "project" "grep" "find") (:url . "https://github.com/re5et/simp"))]) (simple-bookmarks . [(20190204 1426) ((cl-lib (0 5))) "Bookmark / functioncall manager" tar ((:commit . "54e8d771bcdb0eb235b31c0aa9642171369500e5") (:authors ("Julian T. Knabenschuh" . "jtkdevelopments@gmail.com")) (:maintainers ("Julian T. Knabenschuh" . "jtkdevelopments@gmail.com")) (:maintainer "Julian T. Knabenschuh" . "jtkdevelopments@gmail.com") (:keywords "bookmark" "functioncall") (:url . "https://github.com/jtkDvlp/simple-bookmarks"))]) - (simple-call-tree . [(20210625 2001) ((emacs (24 3)) (anaphora (1 0 0))) "analyze source code based on font-lock text-properties" tar ((:commit . "26de24bcde0eae911a0185bb5a6b74b9864fcfc3") (:authors ("Joe Bloggs" . "vapniks@yahoo.com")) (:maintainers ("Joe Bloggs" . "vapniks@yahoo.com")) (:maintainer "Joe Bloggs" . "vapniks@yahoo.com") (:keywords "programming") (:url . "http://www.emacswiki.org/emacs/download/simple-call-tree.el"))]) + (simple-call-tree . [(20240603 11) ((emacs (24 3)) (anaphora (1 0 0))) "analyze source code based on font-lock text-properties" tar ((:commit . "0f810a2cc6859b9de9921fb796be49fae9efd100") (:authors ("Joe Bloggs" . "vapniks@yahoo.com")) (:maintainers ("Joe Bloggs" . "vapniks@yahoo.com")) (:maintainer "Joe Bloggs" . "vapniks@yahoo.com") (:keywords "programming") (:url . "http://www.emacswiki.org/emacs/download/simple-call-tree.el"))]) (simple-httpd . [(20230821 1458) ((cl-lib (0 3))) "pure elisp HTTP server" tar ((:commit . "347c30494d3bcfc79de35e54538f92f4e4a46ecd") (:authors ("Christopher Wellons" . "wellons@nullprogram.com")) (:maintainers ("Christopher Wellons" . "wellons@nullprogram.com")) (:maintainer "Christopher Wellons" . "wellons@nullprogram.com") (:url . "https://github.com/skeeto/emacs-http-server"))]) (simple-indentation . [(20230625 1610) ((emacs (24 3)) (dash (2 18 0)) (s (1 12 0))) "Simplify writing indentation functions, alternative to SMIE" tar ((:commit . "b5f97fc14b3f494cfe009938cf5ee9016a83d30e") (:authors ("Semen Khramtsov" . "hrams205@gmail.com")) (:maintainers ("Semen Khramtsov" . "hrams205@gmail.com")) (:maintainer "Semen Khramtsov" . "hrams205@gmail.com") (:url . "https://github.com/semenInRussia/simple-indentation.el"))]) (simple-modeline . [(20210312 1048) ((emacs (26 1))) "A simple mode-line configuration for Emacs" tar ((:commit . "119d8224a8ae0ee17b09ac1fed6cdb9cb1d048fd") (:authors ("Eder Elorriaga" . "gexplorer8@gmail.com")) (:maintainers ("Eder Elorriaga" . "gexplorer8@gmail.com")) (:maintainer "Eder Elorriaga" . "gexplorer8@gmail.com") (:keywords "mode-line" "faces") (:url . "https://github.com/gexplorer/simple-modeline"))]) @@ -4839,7 +4840,7 @@ (slack . [(20211129 310) ((websocket (1 8)) (request (0 2 0)) (oauth2 (0 10)) (circe (2 2)) (alert (1 2)) (emojify (0 2))) "Slack client for Emacs" tar ((:commit . "ff46d88726482211e3ac3d0b9c95dd4fdffe11c2") (:authors ("yuya.minami" . "yuya.minami@yuyaminami-no-MacBook-Pro.local")) (:maintainer "yuya.minami" . "yuya.minami@yuyaminami-no-MacBook-Pro.local") (:keywords "tools") (:url . "https://github.com/yuya373/emacs-slack"))]) (slideview . [(20150324 2240) ((cl-lib (0 3))) "File slideshow" tar ((:commit . "b6d170bda139aedf81b47dc55cbd1a3af512fb4c") (:authors ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainers ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainer "Masahiro Hayashi" . "mhayashi1120@gmail.com") (:keywords "files") (:url . "https://github.com/mhayashi1120/Emacs-slideview"))]) (slim-mode . [(20240513 2118) nil "Major mode for editing Slim files" tar ((:commit . "8c92169817f2fa59255f547f0a9fb4fbb8309db9") (:authors ("Nathan Weizenbaum")) (:maintainers ("Nathan Weizenbaum")) (:maintainer "Nathan Weizenbaum") (:keywords "markup" "language") (:url . "http://github.com/slim-template/emacs-slim"))]) - (slime . [(20240527 1527) ((emacs (24 3)) (macrostep (0 9))) "Superior Lisp Interaction Mode for Emacs" tar ((:commit . "e261cc83b2a4675824c196965d060b135f6777ad") (:keywords "languages" "lisp" "slime") (:url . "https://github.com/slime/slime"))]) + (slime . [(20240605 1810) ((emacs (24 3)) (macrostep (0 9))) "Superior Lisp Interaction Mode for Emacs" tar ((:commit . "81b6508cdf344b72fe4af1eadbc0194d64163643") (:keywords "languages" "lisp" "slime") (:url . "https://github.com/slime/slime"))]) (slime-company . [(20210124 1627) ((emacs (24 4)) (slime (2 13)) (company (0 9 0))) "slime completion backend for company mode" tar ((:commit . "f20ecc4104d4c35052696e7e760109fb02060e72") (:authors ("Ole Arndt" . "anwyn@sugarshark.com")) (:maintainers ("Ole Arndt" . "anwyn@sugarshark.com")) (:maintainer "Ole Arndt" . "anwyn@sugarshark.com") (:keywords "convenience" "lisp" "abbrev"))]) (slime-docker . [(20210426 1422) ((emacs (24 4)) (slime (2 16)) (docker-tramp (0 1))) "Integration of SLIME with Docker containers" tar ((:commit . "c7d073720f2bd8e9f72a20309fff2afa4c4e798d") (:keywords "docker" "lisp" "slime") (:url . "https://gitlab.common-lisp.net/cl-docker-images/slime-docker"))]) (slime-repl-ansi-color . [(20230214 1453) ((emacs (24)) (slime (2 3 1))) "Turn on ANSI colors in REPL output;" tar ((:commit . "9e8af90490332217e45d7568f1690df3f4e25d4b") (:authors ("Max Mikhanosha" . "max@openchat.com")) (:maintainers ("Augustin Fabre" . "augustin@augfab.fr")) (:maintainer "Augustin Fabre" . "augustin@augfab.fr") (:keywords "lisp") (:url . "https://gitlab.com/augfab/slime-repl-ansi-color"))]) @@ -4927,9 +4928,9 @@ (sonic-pi . [(20211214 1242) ((cl-lib (0 5)) (osc (0 1)) (dash (2 2 0)) (emacs (24)) (highlight (0))) "A Emacs client for SonicPi" tar ((:commit . "9ae16d0fd4cba77ae0bedac83f2cb46569be6ade") (:authors ("Joseph Wilk" . "joe@josephwilk.net")) (:maintainers ("Joseph Wilk" . "joe@josephwilk.net")) (:maintainer "Joseph Wilk" . "joe@josephwilk.net") (:keywords "sonicpi" "ruby") (:url . "http://www.github.com/repl-electric/sonic-pi.el"))]) (soong-mode . [(20221217 1243) ((emacs (27 1))) "Major mode for editing Soong build files" tar ((:commit . "bf3dc1070b368b413958f54fbe9bcc2aaf77b56f") (:authors ("Sergey Bobrenok" . "bobrofon@gmail.com")) (:maintainers ("Sergey Bobrenok" . "bobrofon@gmail.com")) (:maintainer "Sergey Bobrenok" . "bobrofon@gmail.com") (:keywords "languages") (:url . "https://github.com/bobrofon/soong-mode"))]) (soothe-theme . [(20240415 837) ((emacs (24 3)) (autothemer (0 2))) "A dark colorful theme" tar ((:commit . "a8d3d964cfe9fc2157f45d2d26647a450ed9161a") (:authors ("Jason Milkins" . "jasonm23@gmail.com")) (:maintainers ("Jason Milkins" . "jasonm23@gmail.com")) (:maintainer "Jason Milkins" . "jasonm23@gmail.com") (:url . "https://github.com/emacsfodder/emacs-soothe-theme"))]) - (sops . [(20240315 2226) ((emacs (28 1)) (s (1 13 0))) "SOPS encrypt and decrypt without leaving the editor" tar ((:commit . "9ed9f02ff83ab6e0cb0173f0578be1a5b71e3b66") (:authors ("Jonathan Carroll Otsuka" . "pitas.axioms0c@icloud.com")) (:maintainers ("Jonathan Carroll Otsuka" . "pitas.axioms0c@icloud.com")) (:maintainer "Jonathan Carroll Otsuka" . "pitas.axioms0c@icloud.com") (:keywords "convenience" "programming") (:url . "http://github.com/djgoku/sops"))]) + (sops . [(20240607 124) ((emacs (28 1)) (s (1 13 0))) "SOPS encrypt and decrypt without leaving the editor" tar ((:commit . "c59c786572167f4bd3b144a1388418317870ac19") (:authors ("Jonathan Carroll Otsuka" . "pitas.axioms0c@icloud.com")) (:maintainers ("Jonathan Carroll Otsuka" . "pitas.axioms0c@icloud.com")) (:maintainer "Jonathan Carroll Otsuka" . "pitas.axioms0c@icloud.com") (:keywords "convenience" "programming") (:url . "http://github.com/djgoku/sops"))]) (sorcery-theme . [(20210101 1352) ((autothemer (0 2))) "A D&D (Dark and Dusty) Theme" tar ((:commit . "5a1c4445b9e6e09589a299a9962a6973272a0c2f") (:authors ("Maxime Tréca" . "maxime@gmail.com")) (:maintainers ("Maxime Tréca" . "maxime@gmail.com")) (:maintainer "Maxime Tréca" . "maxime@gmail.com") (:url . "http://github.com/vxid/emacs-theme-sorcery"))]) - (soria-theme . [(20230227 1454) ((emacs (25 1))) "A xoria256 theme with some colors from openSUSE" tar ((:commit . "c5275d02fcc9f6af2cfebd69bcf69f8cdccbe3ab") (:authors ("Miquel Sabaté Solà" . "mikisabate@gmail.com")) (:maintainers ("Miquel Sabaté Solà" . "mikisabate@gmail.com")) (:maintainer "Miquel Sabaté Solà" . "mikisabate@gmail.com") (:keywords "faces") (:url . "https://github.com/mssola/soria"))]) + (soria-theme . [(20240603 903) ((emacs (25 1))) "A xoria256 theme with some colors from openSUSE" tar ((:commit . "c6dbabc1b4f956e19c7e80f16e69f3d6c57b84b4") (:authors ("Miquel Sabaté Solà" . "mikisabate@gmail.com")) (:maintainers ("Miquel Sabaté Solà" . "mikisabate@gmail.com")) (:maintainer "Miquel Sabaté Solà" . "mikisabate@gmail.com") (:keywords "faces") (:url . "https://github.com/mssola/soria"))]) (sort-words . [(20160929 1335) nil "Sort words in a selected region" tar ((:commit . "7b6e108f80237363faf7ec28b2c58dec270b8601") (:authors ("\"Aleksandar Simic\"" . "asimic@gmail.com")) (:maintainers ("\"Aleksandar Simic\"" . "asimic@gmail.com")) (:maintainer "\"Aleksandar Simic\"" . "asimic@gmail.com") (:keywords "tools") (:url . "http://github.org/dotemacs/sort-words.el"))]) (sotclojure . [(20170922 8) ((emacs (24 1)) (clojure-mode (4 0 0)) (cider (0 8)) (sotlisp (1 3))) "Write clojure at the speed of thought." tar ((:commit . "ceac82aa691e8d98946471be6aaff9c9a4603c32") (:authors ("Artur Malabarba" . "emacs@endlessparentheses.com")) (:maintainers ("Artur Malabarba" . "emacs@endlessparentheses.com")) (:maintainer "Artur Malabarba" . "emacs@endlessparentheses.com") (:keywords "convenience" "clojure") (:url . "https://github.com/Malabarba/speed-of-thought-clojure"))]) (sotlisp . [(20220909 803) ((emacs (24 1))) "Write lisp at the speed of thought." tar ((:commit . "04186129f2dccf48e288639b78adeb9c0e94be54") (:authors ("Artur Malabarba" . "emacs@endlessparentheses.com")) (:maintainers ("Artur Malabarba" . "emacs@endlessparentheses.com")) (:maintainer "Artur Malabarba" . "emacs@endlessparentheses.com") (:keywords "convenience" "lisp") (:url . "https://github.com/Malabarba/speed-of-thought-lisp"))]) @@ -4966,6 +4967,7 @@ (spice-mode . [(20220210 1414) ((emacs (24 3))) "Major mode for SPICE" tar ((:commit . "f55c2b6dd35caace0ec7250b5c7b5d119235a23d") (:authors ("Geert A. M. Van der Plas" . "geert_vanderplas@email.com") ("Emmanuel Rouat" . "emmanuel.rouat@wanadoo.fr") ("Carlin J. Vieri, MIT AI Lab" . "cvieri@ai.mit.edu")) (:maintainers ("Geert A. M. Van der Plas" . "geert_vanderplas@email.com")) (:maintainer "Geert A. M. Van der Plas" . "geert_vanderplas@email.com") (:keywords "spice" "spice2g6" "spice3" "eldo" "hspice" "layla" "mondriaan" "fasthenry" "cdl" "spectre compatibility" "netlist editing") (:url . "https://repo.or.cz/spice-mode.git"))]) (splitjoin . [(20150505 1432) ((cl-lib (0 5))) "Transition between multiline and single-line code" tar ((:commit . "39a77f1c6c7406e79095eb0385667097172a770c") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainers ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-splitjoin"))]) (splitter . [(20170809 2208) nil "Manage window splits" tar ((:commit . "6bdb51e9a346907d60a9625f6180bddd06be6674") (:authors ("Steven Thomas")) (:maintainers ("Steven Thomas")) (:maintainer "Steven Thomas") (:keywords "frames" "convenience") (:url . "https://github.com/chumpage/chumpy-windows"))]) + (splunk-mode . [(20240422 828) ((emacs (27 1))) "Major Mode for editing Splunk SPL source code" tar ((:commit . "3a9b114fdbabb6e7d6206b1863c54de438bf506b") (:authors ("Jake Ireland" . "jakewilliami@icloud.com")) (:maintainers ("Jake Ireland" . "jakewilliami@icloud.com")) (:maintainer "Jake Ireland" . "jakewilliami@icloud.com") (:keywords "languages" "splunk" "mode") (:url . "https://github.com/jakewilliami/splunk-mode/"))]) (spotify . [(20200615 1418) ((cl-lib (0 5))) "Control the spotify application from emacs" tar ((:commit . "7e28ef0b4519c6a46fce6a89c0ff1ed775eda71a") (:authors ("R.W. van 't Veer")) (:maintainers ("R.W. van 't Veer")) (:maintainer "R.W. van 't Veer") (:keywords "convenience") (:url . "https://github.com/remvee/spotify-el"))]) (spotlight . [(20200109 2137) ((emacs (24 1)) (swiper (0 6 0)) (counsel (0 6 0))) "search files with Mac OS X spotlight" tar ((:commit . "ea71f4fd380c51e50c47bb25855af4f40e4d8da0") (:authors ("Ben Maughan" . "benmaughan@gmail.com")) (:maintainers ("Ben Maughan" . "benmaughan@gmail.com")) (:maintainer "Ben Maughan" . "benmaughan@gmail.com") (:keywords "search" "external") (:url . "http://www.pragmaticemacs.com"))]) (spray . [(20160304 2220) nil "a speed reading mode" tar ((:commit . "69fe48e7bb079e3011476b9f4eb6ac9ae94d6d9b") (:authors ("Ian Kelling" . "ian@iankelling.org")) (:maintainers ("Ian Kelling" . "ian@iankelling.org")) (:maintainer "Ian Kelling" . "ian@iankelling.org") (:keywords "convenience") (:url . "https://github.com/ian-kelling/spray"))]) @@ -4986,7 +4988,7 @@ (sqlite3 . [(20231124 1326) ((emacs (25 1))) "Direct access to the core SQLite3 API" tar ((:commit . "a601c9965e4d0178705a64b7d4f88709ca9aea66") (:authors ("Y. N. Lo" . "elisp@fastmail.com")) (:maintainers ("Y. N. Lo" . "elisp@fastmail.com")) (:maintainer "Y. N. Lo" . "elisp@fastmail.com") (:keywords "comm" "data" "sql") (:url . "https://github.com/pekingduck/emacs-sqlite3-api"))]) (sqlup-mode . [(20170610 1537) nil "Upcase SQL words for you" tar ((:commit . "04970977b4abb4d44301651618bbf1cdb0b263dd") (:authors ("Aldric Giacomoni" . "trevoke@gmail.com")) (:maintainers ("Aldric Giacomoni" . "trevoke@gmail.com")) (:maintainer "Aldric Giacomoni" . "trevoke@gmail.com") (:keywords "sql" "tools" "redis" "upcase") (:url . "https://github.com/trevoke/sqlup-mode.el"))]) (squirrel-mode . [(20221227 232) ((emacs (24 3))) "A major mode for the Squirrel programming language" tar ((:commit . "1af79dfe70c4c8e6f0f144bfd2eb65c077aca785") (:authors ("XXIV")) (:maintainers ("XXIV")) (:maintainer "XXIV") (:keywords "files" "squirrel") (:url . "https://github.com/thechampagne/squirrel-mode"))]) - (sr-speedbar . [(20161025 831) nil "Same frame speedbar" tar ((:commit . "77a83fb50f763a465c021eca7343243f465b4a47") (:authors ("Sebastian Rose" . "sebastian_rose@gmx.de")) (:maintainers ("Sebastian Rose" . "sebastian_rose@gmx.de")) (:maintainer "Sebastian Rose" . "sebastian_rose@gmx.de") (:keywords "speedbar" "sr-speedbar.el") (:url . "http://www.emacswiki.org/emacs/download/sr-speedbar.el"))]) + (sr-speedbar . [(20220705 1231) nil "Same frame speedbar" tar ((:commit . "73ecfc21cf38f0cb1dfbbebebdc3cf573eccf7d2") (:authors ("Sebastian Rose" . "sebastian_rose@gmx.de")) (:maintainers ("Sebastian Rose" . "sebastian_rose@gmx.de")) (:maintainer "Sebastian Rose" . "sebastian_rose@gmx.de") (:keywords "speedbar" "sr-speedbar.el") (:url . "http://www.emacswiki.org/emacs/download/sr-speedbar.el"))]) (srcery-theme . [(20240220 805) ((emacs (24))) "Dark color theme" tar ((:commit . "60028633c5722e6b8ea12844618be0e9b31be55a") (:authors ("Daniel Berg")) (:maintainers ("Daniel Berg")) (:maintainer "Daniel Berg") (:keywords "faces") (:url . "https://github.com/srcery-colors/srcery-emacs"))]) (srefactor . [(20230504 617) ((emacs (24 4))) "A refactoring tool based on Semantic parser framework" tar ((:commit . "95c70a94b5aad4c85b35569e2f2325047791153a") (:authors ("Tu, Do Hoang" . "tuhdo1710@gmail.com")) (:maintainers ("Tu, Do Hoang")) (:maintainer "Tu, Do Hoang") (:keywords "c" "languages" "tools") (:url . "https://github.com/tuhdo/semantic-refactor"))]) (srfi . [(20240507 58) ((emacs (25 1))) "Scheme Requests for Implementation browser" tar ((:commit . "7d3584b0b887a3bedebe5fd829fa7623c1d730fc") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainers ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "languages" "util") (:url . "https://github.com/srfi-explorations/emacs-srfi"))]) @@ -5069,7 +5071,7 @@ (sweetgreen . [(20180605 335) ((dash (2 12 1)) (helm (1 5 6)) (request (0 2 0)) (cl-lib (0 5))) "Order Salads from sweetgreen.com" tar ((:commit . "e933fe466b5ef0e976967e203f88bd7a012469d1") (:authors ("Diego Berrocal" . "cestdiego@gmail.com")) (:maintainers ("Diego Berrocal" . "cestdiego@gmail.com")) (:maintainer "Diego Berrocal" . "cestdiego@gmail.com") (:keywords "salad" "food" "sweetgreen" "request") (:url . "https://www.github.com/CestDiego/sweetgreen.el"))]) (swift-helpful . [(20220707 846) ((emacs (25 1)) (dash (2 12 0)) (lsp-mode (6 0)) (swift-mode (8 0 0))) "Show documentation for Swift programs." tar ((:commit . "b46c580e4b8f55761431ec677866de3fc66592e9") (:authors ("Daniel Martín" . "mardani29@yahoo.es")) (:maintainers ("Daniel Martín" . "mardani29@yahoo.es")) (:maintainer "Daniel Martín" . "mardani29@yahoo.es") (:keywords "help" "swift") (:url . "https://github.com/danielmartin/swift-helpful"))]) (swift-mode . [(20240217 631) ((emacs (24 4)) (seq (2 3))) "Major-mode for Apple's Swift programming language" tar ((:commit . "25cf8237312bf5eddc2c90001feb8f73633ab523") (:authors ("taku0" . "mxxouy6x3m_github@tatapa.org") ("Chris Barrett" . "chris.d.barrett@me.com") ("Bozhidar Batsov" . "bozhidar@batsov.com") ("Arthur Evstifeev" . "lod@pisem.net")) (:maintainers ("taku0" . "mxxouy6x3m_github@tatapa.org")) (:maintainer "taku0" . "mxxouy6x3m_github@tatapa.org") (:keywords "languages" "swift") (:url . "https://github.com/swift-emacs/swift-mode"))]) - (swift-ts-mode . [(20240414 949) ((emacs (29 1))) "Major mode for Swift based on tree-sitter" tar ((:commit . "a62f4d84b836fe208d912c26fb561c93a0c8e296") (:authors ("Martin Rechsteiner")) (:maintainers ("Martin Rechsteiner")) (:maintainer "Martin Rechsteiner") (:keywords "swift" "languages" "tree-sitter") (:url . "https://github.com/rechsteiner/swift-ts-mode"))]) + (swift-ts-mode . [(20240603 735) ((emacs (29 1))) "Major mode for Swift based on tree-sitter" tar ((:commit . "5e198d306bd4d49e4dddab39195fe99e1caf8892") (:authors ("Martin Rechsteiner")) (:maintainers ("Martin Rechsteiner")) (:maintainer "Martin Rechsteiner") (:keywords "swift" "languages" "tree-sitter") (:url . "https://github.com/rechsteiner/swift-ts-mode"))]) (swift3-mode . [(20160918 1250) ((emacs (24 4))) "Major-mode for Apple's Swift programming language." tar ((:commit . "ea34d46bf9a4293e75ffdac9500d34989316d9e9") (:keywords "languages" "swift") (:url . "https://github.com/taku0/swift3-mode"))]) (swiper . [(20240520 1202) ((emacs (24 5)) (ivy (0 14 2))) "Isearch with an overview. Oh, man!" tar ((:commit . "c8808d88c633fdd00f7671fee054954f3a7598b8") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainers ("Basil L. Contovounesios" . "basil@contovou.net")) (:maintainer "Basil L. Contovounesios" . "basil@contovou.net") (:keywords "matching") (:url . "https://github.com/abo-abo/swiper"))]) (swiper-helm . [(20180131 1744) ((emacs (24 1)) (swiper (0 1 0)) (helm (1 5 3))) "Helm version of Swiper." tar ((:commit . "93fb6db87bc6a5967898b5fd3286954cc72a0008") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainers ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "matching") (:url . "https://github.com/abo-abo/swiper-helm"))]) @@ -5085,7 +5087,7 @@ (symbolist . [(20211107 1615) ((emacs (24 5))) "List and interactively unbind Emacs Lisp symbols" tar ((:commit . "92b712734941a45da7d47fd61b95e4013ff53481") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainers ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "lisp" "maint") (:url . "https://github.com/lassik/emacs-symbolist"))]) (symbols-outline . [(20240517 819) ((emacs (27 1))) "Display symbols (functions, variables, etc) in outline view" tar ((:commit . "bfebe73b1322cdc32353375b55f5f56aad85fb57") (:authors ("Shihao Liu")) (:maintainers ("Shihao Liu")) (:maintainer "Shihao Liu") (:keywords "outlines") (:url . "https://github.com/liushihao456/symbols-outline.el"))]) (symbolword-mode . [(20180401 1427) ((emacs (24)) (f (0 19 0))) "modify word split" tar ((:commit . "920e57f4c2b09b28c5a0c8fe9ebdba9961822163") (:authors ("ncaq" . "ncaq@ncaq.net")) (:maintainers ("ncaq" . "ncaq@ncaq.net")) (:maintainer "ncaq" . "ncaq@ncaq.net") (:url . "https://github.com/ncaq/symbolword-mode"))]) - (symex . [(20240418 806) ((emacs (25 1)) (tsc (0 15 2)) (tree-sitter (0 15 2)) (lispy (0 26 0)) (paredit (24)) (evil-cleverparens (20170718 413)) (evil (1 2 14)) (evil-surround (1 0 4)) (hydra (0 15 0)) (seq (2 22))) "An evil way to edit Lisp symbolic expressions as trees" tar ((:commit . "db8ac5b3039b91e28d4c403c98fcb799ec94369e") (:authors ("Siddhartha Kasivajhula" . "sid@countvajhula.com")) (:maintainers ("Siddhartha Kasivajhula" . "sid@countvajhula.com")) (:maintainer "Siddhartha Kasivajhula" . "sid@countvajhula.com") (:keywords "lisp" "convenience" "languages") (:url . "https://github.com/countvajhula/symex.el"))]) + (symex . [(20240604 1738) ((emacs (25 1)) (tsc (0 15 2)) (tree-sitter (0 15 2)) (lispy (0 26 0)) (paredit (24)) (evil-cleverparens (20170718 413)) (evil (1 2 14)) (evil-surround (1 0 4)) (hydra (0 15 0)) (seq (2 22))) "An evil way to edit Lisp symbolic expressions as trees" tar ((:commit . "c18a1d1b0b6d13f36a94eb18828b6b25822c410f") (:authors ("Siddhartha Kasivajhula" . "sid@countvajhula.com")) (:maintainers ("Siddhartha Kasivajhula" . "sid@countvajhula.com")) (:maintainer "Siddhartha Kasivajhula" . "sid@countvajhula.com") (:keywords "lisp" "convenience" "languages") (:url . "https://github.com/countvajhula/symex.el"))]) (symon . [(20170224 833) nil "tiny graphical system monitor" tar ((:commit . "76461679dfe13a5dccd3c8735fb6f58b26b46733") (:authors ("zk_phi")) (:maintainers ("zk_phi")) (:maintainer "zk_phi") (:url . "http://hins11.yu-yake.com/"))]) (symon-lingr . [(20150719 1342) ((symon (1 1 2)) (cl-lib (0 5))) "A notification-based Lingr client powered by symon.el" tar ((:commit . "056d1a473e36992ff5881e5ce6fdc331cead975f") (:authors ("zk_phi")) (:maintainers ("zk_phi")) (:maintainer "zk_phi") (:url . "http://hins11.yu-yake.com/"))]) (sync-recentf . [(20160326 2001) nil "Synchronize the recent files list between Emacs instances" tar ((:commit . "0052561d5c5b5c2684faedc3eead776aec06c3ed") (:authors ("François Févotte" . "fevotte@gmail.com")) (:maintainers ("François Févotte" . "fevotte@gmail.com")) (:maintainer "François Févotte" . "fevotte@gmail.com") (:keywords "recentf") (:url . "https://github.com/ffevotte/sync-recentf"))]) @@ -5156,9 +5158,9 @@ (term+mux . [(20140211 749) ((term+ (0 1)) (tab-group (0 1))) "term+ terminal multiplexer and session management" tar ((:commit . "81b60e80cf008472bfd7fad9233af2ef722c208a") (:authors ("INA Lintaro <tarao.gnn at gmail.com>")) (:maintainers ("INA Lintaro <tarao.gnn at gmail.com>")) (:maintainer "INA Lintaro <tarao.gnn at gmail.com>") (:keywords "terminal" "emulation") (:url . "http://github.com/tarao/term+-el"))]) (term-alert . [(20230407 1715) ((emacs (24 0)) (term-cmd (1 1)) (alert (1 1)) (f (0 18 2))) "Notifications when commands complete in term.el." tar ((:commit . "8e7e744773e41355bcd9f5c911001be08bc79bec") (:authors ("Callie Cameron" . "cjcameron7@gmail.com")) (:maintainer "Callie Cameron" . "cjcameron7@gmail.com") (:keywords "notifications" "processes") (:url . "https://github.com/calliecameron/term-alert"))]) (term-cmd . [(20230407 1704) ((emacs (27 2)) (dash (2 12 0)) (f (0 18 2))) "Send commands from programs running in term.el." tar ((:commit . "26c5a8cb6b55ac0d6c6bc08f6ea1b1e53f6e2654") (:authors ("Callie Cameron" . "cjcameron7@gmail.com")) (:maintainer "Callie Cameron" . "cjcameron7@gmail.com") (:keywords "processes") (:url . "https://github.com/calliecameron/term-cmd"))]) - (term-manager . [(20230727 2308) ((dash (2 12 0)) (emacs (24 4))) "Contextual terminal management" tar ((:commit . "31a3d16ba5a4f9e6f4bc52275eaedf55a96154a8") (:authors ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainers ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "terminals" "tools") (:url . "https://www.github.com/IvanMalison/term-manager"))]) - (term-project . [(20230727 2308) ((emacs (28 1)) (term-manager (0 1 0))) "Terminal management for project.el" tar ((:commit . "31a3d16ba5a4f9e6f4bc52275eaedf55a96154a8") (:authors ("Ivan Malison" . "IvanMalison@gmail.com") ("ROCKTAKEY" . "rocktakey@gmail.com")) (:maintainers ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "project" "tools" "terminals" "vc") (:url . "https://www.github.com/IvanMalison/term-manager"))]) - (term-projectile . [(20230727 2308) ((emacs (24)) (term-manager (0 1 0)) (projectile (0 13 0))) "projectile terminal management" tar ((:commit . "31a3d16ba5a4f9e6f4bc52275eaedf55a96154a8") (:authors ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainers ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "projectile" "tools" "terminals" "vc") (:url . "https://www.github.com/IvanMalison/term-manager"))]) + (term-manager . [(20240602 2356) ((dash (2 12 0)) (emacs (24 4))) "Contextual terminal management" tar ((:commit . "25353734c65cd5cc952e4893b552629ca1d0d37f") (:authors ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainers ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "terminals" "tools") (:url . "https://www.github.com/IvanMalison/term-manager"))]) + (term-project . [(20240602 2356) ((emacs (28 1)) (term-manager (0 1 0))) "Terminal management for project.el" tar ((:commit . "25353734c65cd5cc952e4893b552629ca1d0d37f") (:authors ("Ivan Malison" . "IvanMalison@gmail.com") ("ROCKTAKEY" . "rocktakey@gmail.com")) (:maintainers ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "project" "tools" "terminals" "vc") (:url . "https://www.github.com/IvanMalison/term-manager"))]) + (term-projectile . [(20240602 2356) ((emacs (24)) (term-manager (0 1 0)) (projectile (0 13 0))) "projectile terminal management" tar ((:commit . "25353734c65cd5cc952e4893b552629ca1d0d37f") (:authors ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainers ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "projectile" "tools" "terminals" "vc") (:url . "https://www.github.com/IvanMalison/term-manager"))]) (term-run . [(20200128 702) nil "Run arbitrary command in terminal buffer" tar ((:commit . "0fd135d55fcf864598b1fb8dd880833a1a322910") (:authors ("10sr <8slashes+el [at] gmail [dot] com>")) (:maintainers ("10sr <8slashes+el [at] gmail [dot] com>")) (:maintainer "10sr <8slashes+el [at] gmail [dot] com>") (:keywords "utility" "shell" "command" "term-mode") (:url . "https://github.com/10sr/term-run-el"))]) (termbright-theme . [(20151031 235) ((emacs (24 1))) "a more usable theme for white-on-black terminals" tar ((:commit . "bec6ab14336c0611e85f45486276004f16d20607") (:authors ("Brian Mastenbrook" . "brian@mastenbrook.net")) (:maintainers ("Brian Mastenbrook" . "brian@mastenbrook.net")) (:maintainer "Brian Mastenbrook" . "brian@mastenbrook.net") (:keywords "themes") (:url . "https://github.com/bmastenbrook/termbright-theme-el"))]) (terminal-focus-reporting . [(20180830 719) ((emacs (24 4))) "Minor mode for terminal focus reporting." tar ((:commit . "8b84bf18f4c5f1b59a11692eb706f13c3598d9a5") (:authors ("Vitalii Elenhaupt")) (:maintainers ("Vitalii Elenhaupt")) (:maintainer "Vitalii Elenhaupt") (:keywords "convenience") (:url . "https://github.com/veelenga/terminal-focus-reporting.el"))]) @@ -5172,7 +5174,7 @@ (tesouro . [(20221003 1303) ((request (0 3 2)) (emacs (24 4))) "Brazilian Portuguese synonym search in dicio.com.br" tar ((:commit . "3dbfc49209237215163be1ea338dea099ddc0795") (:url . "https://github.com/rberaldo/tesouro.el"))]) (test-c . [(20180423 1720) ((emacs (24 3))) "quickly test c code" tar ((:commit . "761a576f62c7021ba941f178f153c51289df1553") (:authors ("Aurélien Aptel" . "aurelien.aptel@gmail.com")) (:maintainers ("Aurélien Aptel" . "aurelien.aptel@gmail.com")) (:maintainer "Aurélien Aptel" . "aurelien.aptel@gmail.com") (:url . "http://github.com/aaptel/test-c"))]) (test-case-mode . [(20130525 1434) ((fringe-helper (0 1 1))) "unit test front-end" tar ((:commit . "26e397c0f930b7eb0be413ef7dd257b1da052bec") (:authors ("Nikolaj Schumacher <bugs * nschum de>")) (:maintainers ("Nikolaj Schumacher <bugs * nschum de>")) (:maintainer "Nikolaj Schumacher <bugs * nschum de>") (:keywords "tools") (:url . "http://nschum.de/src/emacs/test-case-mode/"))]) - (test-cockpit . [(20240601 1545) ((emacs (28 1)) (projectile (2 7)) (toml (20230411 1449))) "A command center to run tests of a software project" tar ((:commit . "ad7d4f02d9fe438449befeecf193a547760c0797") (:authors ("Johannes Mueller" . "github@johannes-mueller.org")) (:maintainers ("Johannes Mueller" . "github@johannes-mueller.org")) (:maintainer "Johannes Mueller" . "github@johannes-mueller.org") (:url . "https://github.com/johannes-mueller/test-cockpit.el"))]) + (test-cockpit . [(20240604 1943) ((emacs (28 1)) (projectile (2 7)) (toml (20230411 1449))) "A command center to run tests of a software project" tar ((:commit . "068d3a393cebdc871236b8d1e45e06f997e2b0d0") (:authors ("Johannes Mueller" . "github@johannes-mueller.org")) (:maintainers ("Johannes Mueller" . "github@johannes-mueller.org")) (:maintainer "Johannes Mueller" . "github@johannes-mueller.org") (:url . "https://github.com/johannes-mueller/test-cockpit.el"))]) (test-kitchen . [(20171129 2035) nil "Run test-kitchen inside of emacs" tar ((:commit . "0fc0ca4808425f03fbeb8125246043723e2a179a") (:authors ("JJ Asghar")) (:maintainers ("JJ Asghar")) (:maintainer "JJ Asghar") (:keywords "chef" "ruby" "test-kitchen") (:url . "http://github.com/jjasghar/test-kitchen-el"))]) (test-simple . [(20230916 1634) ((cl-lib (0))) "Simple Unit Test Framework for Emacs Lisp" tar ((:commit . "8b191842318bb05da74052025192d32ebebb033a") (:authors ("Rocky Bernstein" . "rocky@gnu.org")) (:maintainers ("Rocky Bernstein" . "rocky@gnu.org")) (:maintainer "Rocky Bernstein" . "rocky@gnu.org") (:keywords "unit-test") (:url . "https://github.com/rocky/emacs-test-simple"))]) (tex-smart-umlauts . [(20230416 2051) nil "Smart umlaut conversion for TeX." tar ((:commit . "b28bac71990e0442616157fdb64494179df5575e") (:authors ("Frank Fischer <frank-fischer at shadow-soft.de>")) (:maintainers ("Frank Fischer <frank-fischer at shadow-soft.de>")) (:maintainer "Frank Fischer <frank-fischer at shadow-soft.de>") (:keywords "tex" "wp") (:url . "http://hub.darcs.net/lyro/tex-smart-umlauts"))]) @@ -5196,7 +5198,7 @@ (third-time . [(20240207 1621) ((emacs (27 1))) "Third Time: A Better Way to Work" tar ((:commit . "093b74be860fac389fb173caef5fabf61e417eef") (:authors ("Samuel W. Flint" . "swflint@flintfam.org")) (:maintainers ("Samuel W. Flint" . "swflint@flintfam.org")) (:maintainer "Samuel W. Flint" . "swflint@flintfam.org") (:url . "https://git.sr.ht/~swflint/third-time"))]) (thread-dump . [(20170816 1850) nil "Java thread dump viewer" tar ((:commit . "204c9600242756d4b514bb5ff6293e052bf4b49d") (:authors ("Dmitry Neverov")) (:maintainers ("Dmitry Neverov")) (:maintainer "Dmitry Neverov") (:url . "http://github.com/nd/thread-dump.el"))]) (threes . [(20160820 1242) ((emacs (24)) (seq (1 11))) "A clone of Threes (a tiny puzzle game)" tar ((:commit . "6981acb30b856c77cba6aba63fefbf102cbdfbb2") (:authors ("Chunyang Xu" . "xuchunyang.me@gmail.com")) (:maintainers ("Chunyang Xu" . "xuchunyang.me@gmail.com")) (:maintainer "Chunyang Xu" . "xuchunyang.me@gmail.com") (:keywords "games") (:url . "https://github.com/xuchunyang/threes.el"))]) - (thrift . [(20240526 1633) ((emacs (24))) "major mode for fbthrift and Apache Thrift files" tar ((:commit . "f408d11adf2b518358a7fa2cc86c3267354b33d0") (:keywords "languages"))]) + (thrift . [(20240603 846) ((emacs (24))) "major mode for fbthrift and Apache Thrift files" tar ((:commit . "e38e4a90339d119850e97f5dedbe9ce57a89c652") (:keywords "languages"))]) (thumb-through . [(20120119 534) nil "Plain text reader of HTML documents" tar ((:commit . "08d8fb720f93c6172653e035191a8fa9c3305e63") (:keywords "html"))]) (tickscript-mode . [(20171219 203) ((emacs (24 1))) "A major mode for Tickscript files" tar ((:commit . "f0579f38ff14954df5002ce30ae6d4a2c978d461") (:authors ("Marc Sherry" . "msherry@gmail.com")) (:maintainers ("Marc Sherry" . "msherry@gmail.com")) (:maintainer "Marc Sherry" . "msherry@gmail.com") (:keywords "languages") (:url . "https://github.com/msherry/tickscript-mode"))]) (tidal . [(20240407 1952) ((haskell-mode (16)) (emacs (25 1))) "Interact with TidalCycles for live coding patterns" tar ((:commit . "88f09edf6bef2228d5f530dea872b08a9d803066") (:authors (nil . "alex@slab.org")) (:maintainers (nil . "alex@slab.org")) (:maintainer nil . "alex@slab.org") (:keywords "tools") (:url . "https://github.com/tidalcycles/Tidal"))]) @@ -5266,7 +5268,7 @@ (tramp-term . [(20220725 1441) nil "Automatic setup of directory tracking in ssh sessions" tar ((:commit . "ed75189122737d301f716a30a8013205aa3736f1") (:authors ("Randy Morris" . "randy.morris@archlinux.us")) (:maintainers ("Randy Morris" . "randy.morris@archlinux.us")) (:maintainer "Randy Morris" . "randy.morris@archlinux.us") (:keywords "comm" "terminals") (:url . "https://github.com/randymorris/tramp-term.el"))]) (transducers . [(20240308 843) ((emacs (28 1))) "Ergonomic, efficient data processing" tar ((:commit . "2d452e4cdc3b5cfa29ee3d7a645ff53d4e993384") (:authors ("Colin Woodbury" . "colin@fosskers.ca")) (:maintainers ("Colin Woodbury" . "colin@fosskers.ca")) (:maintainer "Colin Woodbury" . "colin@fosskers.ca") (:keywords "lisp") (:url . "https://git.sr.ht/~fosskers/transducers.el"))]) (transfer-sh . [(20200601 1708) ((emacs (24 3)) (async (1 0))) "Simple interface for sending buffer contents to transfer.sh" tar ((:commit . "0621a66d00ec91a209a542c10b158095088bd44d") (:keywords "comm" "convenience" "files") (:url . "https://gitlab.com/tuedachu/transfer-sh.el"))]) - (transient . [(20240525 1118) ((emacs (26 1)) (compat (29 1 4 4)) (seq (2 24))) "Transient commands" tar ((:commit . "99a68578df4d938598d0fcbb8401e2fe35be6132") (:authors ("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev") (:keywords "extensions") (:url . "https://github.com/magit/transient"))]) + (transient . [(20240607 1832) ((emacs (26 1)) (compat (29 1 4 4)) (seq (2 24))) "Transient commands" tar ((:commit . "872b19b062653797e997db4907da59315ed16c5b") (:authors ("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev") (:keywords "extensions") (:url . "https://github.com/magit/transient"))]) (transient-dwim . [(20221225 1630) ((emacs (26 1)) (transient (0 1))) "Useful preset transient commands" tar ((:commit . "cb5e0d35729fc6448553b7a17fc5c843f00e8c1d") (:authors ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainers ("Naoya Yamashita" . "conao3@gmail.com")) (:maintainer "Naoya Yamashita" . "conao3@gmail.com") (:keywords "tools") (:url . "https://github.com/conao3/transient-dwim.el"))]) (transient-extras . [(20230721 839) ((emacs (28 1))) "Extra features for transient" tar ((:commit . "ca0d5c597382615f0ee8300ff8718f54f8214359") (:authors ("Al Haji-Ali <abdo.haji.ali@gmail.com>, Samuel W. Flint" . "swflint@flintfam.org")) (:maintainers ("Al Haji-Ali <abdo.haji.ali@gmail.com>, Samuel W. Flint" . "swflint@flintfam.org")) (:maintainer "Al Haji-Ali <abdo.haji.ali@gmail.com>, Samuel W. Flint" . "swflint@flintfam.org") (:keywords "convenience") (:url . "https://github.com/haji-ali/transient-extras.git"))]) (transient-extras-a2ps . [(20230303 1511) ((emacs (28 1)) (transient-extras (1 0 0))) "A transient interface to a2ps" tar ((:commit . "e91a1cddb1f0cb8b99d2bd30db64d467e5fa7ea8") (:authors ("Samuel W. Flint" . "swflint@flintfam.org")) (:maintainers ("Samuel W. Flint" . "swflint@flintfam.org")) (:maintainer "Samuel W. Flint" . "swflint@flintfam.org") (:keywords "convenience") (:url . "https://git.sr.ht/~swflint/transient-extras-a2ps"))]) @@ -5285,8 +5287,8 @@ (tree-sitter . [(20220212 1632) ((emacs (25 1)) (tsc (0 18 0))) "Incremental parsing system" tar ((:commit . "909717c685ff5a2327fa2ca8fb8a25216129361c") (:authors ("Tuấn-Anh Nguyễn" . "ubolonton@gmail.com")) (:maintainers ("Tuấn-Anh Nguyễn" . "ubolonton@gmail.com")) (:maintainer "Tuấn-Anh Nguyễn" . "ubolonton@gmail.com") (:keywords "languages" "tools" "parsers" "tree-sitter") (:url . "https://github.com/emacs-tree-sitter/elisp-tree-sitter"))]) (tree-sitter-ess-r . [(20221012 855) ((emacs (26 1)) (ess (18 10 1)) (tree-sitter (0 12 1)) (tree-sitter-langs (0 12 0))) "R with tree-sitter" tar ((:commit . "9669c00f3d3463e6769725af74c392891e269eed") (:authors ("Shuguang Sun" . "shuguang79@qq.com")) (:maintainers ("Shuguang Sun" . "shuguang79@qq.com")) (:maintainer "Shuguang Sun" . "shuguang79@qq.com") (:keywords "tools") (:url . "https://github.com/ShuguangSun/tree-sitter-ess-r"))]) (tree-sitter-indent . [(20220411 1439) ((emacs (26 1)) (tree-sitter (0 12 1)) (seq (2 20))) "Provide indentation with a Tree-sitter backend" tar ((:commit . "4ef246db3e4ff99f672fe5e4b416c890f885c09e") (:authors ("Felipe Lema" . "felipelema@mortemale.org")) (:maintainers ("Felipe Lema" . "felipelema@mortemale.org")) (:maintainer "Felipe Lema" . "felipelema@mortemale.org") (:keywords "convenience" "internal") (:url . "https://codeberg.org/FelipeLema/tree-sitter-indent.el"))]) - (tree-sitter-ispell . [(20240522 1356) ((emacs (26 1)) (tree-sitter (0 15 0))) "Run ispell on tree-sitter text nodes" tar ((:commit . "960e68d6c4a296e5ecf10d27bfd8bac42ba4a2ed") (:authors ("Erick Navarro" . "erick@navarro.io")) (:maintainers ("Erick Navarro" . "erick@navarro.io")) (:maintainer "Erick Navarro" . "erick@navarro.io") (:url . "https://github.com/erickgnavar/tree-sitter-ispell.el"))]) - (tree-sitter-langs . [(20240602 731) ((emacs (25 1)) (tree-sitter (0 15 0))) "Grammar bundle for tree-sitter" tar ((:commit . "a7b51c99c44194a853be138ffde1a73360966bae") (:authors ("Tuấn-Anh Nguyễn" . "ubolonton@gmail.com")) (:maintainers ("Tuấn-Anh Nguyễn" . "ubolonton@gmail.com")) (:maintainer "Tuấn-Anh Nguyễn" . "ubolonton@gmail.com") (:keywords "languages" "tools" "parsers" "tree-sitter") (:url . "https://github.com/emacs-tree-sitter/tree-sitter-langs"))]) + (tree-sitter-ispell . [(20240604 54) ((emacs (26 1)) (tree-sitter (0 15 0))) "Run ispell on tree-sitter text nodes" tar ((:commit . "07b555016cbff5bf6b8238ba06246b4074aaf7cf") (:authors ("Erick Navarro" . "erick@navarro.io")) (:maintainers ("Erick Navarro" . "erick@navarro.io")) (:maintainer "Erick Navarro" . "erick@navarro.io") (:url . "https://github.com/erickgnavar/tree-sitter-ispell.el"))]) + (tree-sitter-langs . [(20240609 742) ((emacs (25 1)) (tree-sitter (0 15 0))) "Grammar bundle for tree-sitter" tar ((:commit . "a94cfcf64ea15cc6a6c330bb145d2810f5f771a1") (:authors ("Tuấn-Anh Nguyễn" . "ubolonton@gmail.com")) (:maintainers ("Tuấn-Anh Nguyễn" . "ubolonton@gmail.com")) (:maintainer "Tuấn-Anh Nguyễn" . "ubolonton@gmail.com") (:keywords "languages" "tools" "parsers" "tree-sitter") (:url . "https://github.com/emacs-tree-sitter/tree-sitter-langs"))]) (treebundel . [(20240531 2321) ((emacs (27 1)) (compat (29 1 4 2))) "Bundle related git-worktrees together" tar ((:commit . "b0a5d1bf924d8cadde5bae50b8d9ac131279b828") (:authors ("Ben Whitley")) (:maintainers ("Ben Whitley")) (:maintainer "Ben Whitley") (:keywords "convenience" "vc") (:url . "https://github.com/purplg/treebundel"))]) (treefactor . [(20200516 1631) ((emacs (26 1)) (dash (2 16 0)) (f (0 20 0)) (org (9 2 6)) (avy (0 5 0))) "Restructure your messy Org documents" tar ((:commit . "75357757022a4399ab772ff0d92065bd114dabe9") (:authors ("Leo Littlebook" . "Leo.Littlebook@gmail.com")) (:maintainers ("Leo Littlebook" . "Leo.Littlebook@gmail.com")) (:maintainer "Leo Littlebook" . "Leo.Littlebook@gmail.com") (:keywords "outlines" "files" "convenience") (:url . "https://github.com/cyberthal/treefactor"))]) (treemacs . [(20240518 932) ((emacs (26 1)) (cl-lib (0 5)) (dash (2 11 0)) (s (1 12 0)) (ace-window (0 9 0)) (pfuture (1 7)) (hydra (0 13 2)) (ht (2 2)) (cfrs (1 3 2))) "A tree style file explorer package" tar ((:commit . "923fbbdea57b27ed3293079b13846206add85d9d") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainers ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) @@ -5329,7 +5331,7 @@ (twitch-api . [(20220420 1547) ((emacs (27 1)) (dash (2 19 0))) "An elisp interface for the Twitch.tv API" tar ((:commit . "181681097d1fc8d7b78928f8a5b38c61d0e20ef5") (:keywords "multimedia" "twitch-api") (:url . "https://github.com/BenediktBroich/twitch-api"))]) (twittering-mode . [(20181121 1402) nil "Major mode for Twitter" tar ((:commit . "114891e8fdb4f06b1326a6cf795e49c205cf9e29") (:authors ("Tadashi MATSUO" . "tad@mymail.twin.ne.jp") ("Y. Hayamizu" . "y.hayamizu@gmail.com") ("Tsuyoshi CHO" . "Tsuyoshi.CHO+develop@Gmail.com") ("Alberto Garcia" . "agarcia@igalia.com") ("Xavier Maillard" . "xavier@maillard.im")) (:maintainers ("Tadashi MATSUO" . "tad@mymail.twin.ne.jp")) (:maintainer "Tadashi MATSUO" . "tad@mymail.twin.ne.jp") (:keywords "twitter" "web") (:url . "http://twmode.sf.net/"))]) (twtxt . [(20220628 309) ((emacs (25 1)) (request (0 2 0))) "A twtxt client for Emacs" tar ((:commit . "eb9efa19086fcae343353f6a5e88c3377fd06dd4") (:authors ("DEADBLACKCLOVER" . "deadblackclover@protonmail.com")) (:maintainers ("DEADBLACKCLOVER" . "deadblackclover@protonmail.com")) (:maintainer "DEADBLACKCLOVER" . "deadblackclover@protonmail.com") (:url . "https://github.com/deadblackclover/twtxt-el"))]) - (typescript-mode . [(20240507 617) ((emacs (24 3))) "Major mode for editing typescript" tar ((:commit . "1cf78d7ef8e0a1684a2cf265539c54ccff4068c0") (:keywords "typescript" "languages") (:url . "http://github.com/ananthakumaran/typescript.el"))]) + (typescript-mode . [(20240603 630) ((emacs (24 3))) "Major mode for editing typescript" tar ((:commit . "5bb294411ff06ad40186bb7ca141fdbfff902e09") (:keywords "typescript" "languages") (:url . "http://github.com/ananthakumaran/typescript.el"))]) (typewriter-roll-mode . [(20240225 1412) ((emacs (24 1))) "Aid for distraction-free writing" tar ((:commit . "99afeb13bd0340a23176c4ebfdabc93117c04069") (:authors ("Peter Badida" . "keyweeusr@gmail.com")) (:maintainers ("Peter Badida" . "keyweeusr@gmail.com")) (:maintainer "Peter Badida" . "keyweeusr@gmail.com") (:keywords "convenience" "line" "carriage" "writing" "distraction" "cr" "rewind") (:url . "https://github.com/KeyWeeUsr/typewriter-roll-mode"))]) (typing . [(20180830 2203) nil "The Typing Of Emacs" tar ((:commit . "a2ef25dde2d8eb91bd9c0c6164cb5208208647fa") (:authors ("Alex Schroeder" . "alex@gnu.org")) (:maintainers ("Alex Schroeder" . "alex@gnu.org")) (:maintainer "Alex Schroeder" . "alex@gnu.org") (:keywords "games") (:url . "http://www.emacswiki.org/emacs/TypingOfEmacs"))]) (typing-game . [(20160426 1220) nil "a simple typing game" tar ((:commit . "616435a5270274f4c7b698697674dbb2039049a4") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainers ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "lisp" "game"))]) @@ -5478,8 +5480,8 @@ (voca-builder . [(20161101 1645) ((popup (0 5 2))) "Helps you build up your vocabulary" tar ((:commit . "51573beec8cd8308477b0faf453aad93e17f57c5") (:authors ("Yi Tang" . "yi.tang.uk@me.com")) (:maintainers ("Yi Tang" . "yi.tang.uk@me.com")) (:maintainer "Yi Tang" . "yi.tang.uk@me.com") (:keywords "english" "vocabulary") (:url . "https://github.com/yitang/voca-builder"))]) (volatile-highlights . [(20230301 1402) nil "Minor mode for visual feedback on some operations." tar ((:commit . "fcf6e2778454ce514c189a7d1fe70e03ad81c325") (:authors ("K-talo Miyazaki <Keitaro dot Miyazaki at gmail dot com>")) (:maintainers ("K-talo Miyazaki <Keitaro dot Miyazaki at gmail dot com>")) (:maintainer "K-talo Miyazaki <Keitaro dot Miyazaki at gmail dot com>") (:keywords "emulations" "convenience" "wp") (:url . "http://www.emacswiki.org/emacs/download/volatile-highlights.el"))]) (volume . [(20220904 1727) nil "tweak your sound card volume from Emacs" tar ((:commit . "050d3e6d2543a6771a13f95612055864679b6301") (:authors ("Daniel Brockman" . "daniel@brockman.se")) (:maintainers ("Daniel Brockman" . "daniel@brockman.se")) (:maintainer "Daniel Brockman" . "daniel@brockman.se") (:url . "http://www.brockman.se/software/volume-el/"))]) - (vs-dark-theme . [(20240529 530) ((emacs (24 1))) "Visual Studio IDE dark theme" tar ((:commit . "8f3d8e868cfa5ad8743a723cdba6be154d6a1ae8") (:authors ("Jen-Chieh Shen")) (:maintainers ("Jen-Chieh Shen")) (:maintainer "Jen-Chieh Shen") (:url . "https://github.com/emacs-vs/vs-dark-theme"))]) - (vs-light-theme . [(20240529 531) ((emacs (24 1))) "Visual Studio IDE light theme" tar ((:commit . "fe9079fe76b2daba4a8e26effbe3c1f77cc083ff") (:authors ("Jen-Chieh Shen")) (:maintainers ("Jen-Chieh Shen")) (:maintainer "Jen-Chieh Shen") (:url . "https://github.com/emacs-vs/vs-light-theme"))]) + (vs-dark-theme . [(20240605 134) ((emacs (24 1))) "Visual Studio IDE dark theme" tar ((:commit . "351300bad1a28f5e86f39f6fae9ca5d8a0cfb24d") (:authors ("Jen-Chieh Shen")) (:maintainers ("Jen-Chieh Shen")) (:maintainer "Jen-Chieh Shen") (:url . "https://github.com/emacs-vs/vs-dark-theme"))]) + (vs-light-theme . [(20240605 133) ((emacs (24 1))) "Visual Studio IDE light theme" tar ((:commit . "5eeb6e5df24172497c844da750697b2ca3b750fe") (:authors ("Jen-Chieh Shen")) (:maintainers ("Jen-Chieh Shen")) (:maintainer "Jen-Chieh Shen") (:url . "https://github.com/emacs-vs/vs-light-theme"))]) (vscdark-theme . [(20191212 107) ((emacs (24 1))) "VS Code Dark+ like theme" tar ((:commit . "f419553e2a2f091a8bc257fb5ab520326e93ddd4") (:authors ("Alexander L. Belikoff")) (:maintainers ("Alexander L. Belikoff")) (:maintainer "Alexander L. Belikoff") (:url . "https://github.com/abelikoff/vscdark-theme"))]) (vscode-dark-plus-theme . [(20230725 1703) nil "Default Visual Studio Code Dark+ theme" tar ((:commit . "65420ca73b543e1e7955905bea1a8d7e5fe6c5ff") (:authors ("Ian Y.E. Pan")) (:maintainers ("Ian Y.E. Pan")) (:maintainer "Ian Y.E. Pan") (:url . "https://github.com/ianpan870102/vscode-dark-plus-emacs-theme"))]) (vscode-icon . [(20230330 2206) ((emacs (25 1))) "Utility package to provide Vscode style icons" tar ((:commit . "3976bc2e7e2fe0068ae59c11d226f67e0e87aaea") (:authors ("James Nguyen" . "james@jojojames.com")) (:maintainers ("James Nguyen" . "james@jojojames.com")) (:maintainer "James Nguyen" . "james@jojojames.com") (:keywords "files" "tools") (:url . "https://github.com/jojojames/vscode-icon-emacs"))]) @@ -5587,7 +5589,7 @@ (winum . [(20190911 1607) ((cl-lib (0 5)) (dash (2 13 0))) "Navigate windows and frames using numbers." tar ((:commit . "098249c65042ee0308b8236d1ee838c8da8fdf25") (:authors ("Thomas de Beauchêne" . "thomas.de.beauchene@gmail.com")) (:maintainers ("Thomas de Beauchêne" . "thomas.de.beauchene@gmail.com")) (:maintainer "Thomas de Beauchêne" . "thomas.de.beauchene@gmail.com") (:keywords "convenience" "frames" "windows" "multi-screen") (:url . "http://github.com/deb0ch/winum.el"))]) (wisp-mode . [(20220529 1522) ((emacs (24 4))) "Tools for wisp: the Whitespace-to-Lisp preprocessor" tar ((:commit . "1a01003d400db8a42838cabcb26c06d627246a17") (:authors ("Arne Babenhauserheide" . "arne_bab@web.de")) (:maintainers ("Arne Babenhauserheide" . "arne_bab@web.de")) (:maintainer "Arne Babenhauserheide" . "arne_bab@web.de") (:keywords "languages" "lisp" "scheme") (:url . "http://www.draketo.de/english/wisp"))]) (wispjs-mode . [(20170720 1919) ((clojure-mode (0))) "Major mode for Wisp code." tar ((:commit . "60f9f5fd9d1556e2d008939f67eb1b1d0f325fa8") (:authors ("Kris Jenkins" . "krisajenkins@gmail.com")) (:maintainers ("Kris Jenkins" . "krisajenkins@gmail.com")) (:maintainer "Kris Jenkins" . "krisajenkins@gmail.com") (:url . "https://github.com/krisajenkins/wispjs-mode"))]) - (with-editor . [(20240415 1558) ((emacs (25 1)) (compat (29 1 4 1))) "Use the Emacsclient as $EDITOR" tar ((:commit . "1b4526453ef6bdee30635f469aa26085c02b1ac1") (:authors ("Jonas Bernoulli" . "emacs.with-editor@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.with-editor@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.with-editor@jonas.bernoulli.dev") (:keywords "processes" "terminals") (:url . "https://github.com/magit/with-editor"))]) + (with-editor . [(20240609 1518) ((emacs (25 1)) (compat (29 1 4 1))) "Use the Emacsclient as $EDITOR" tar ((:commit . "f6a3fc8f6735fbc804e02f9c54bc621746afd5b0") (:authors ("Jonas Bernoulli" . "emacs.with-editor@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.with-editor@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.with-editor@jonas.bernoulli.dev") (:keywords "processes" "terminals") (:url . "https://github.com/magit/with-editor"))]) (with-emacs . [(20220814 444) ((emacs (24 4))) "Evaluate Emacs Lisp expressions in a separate Emacs process" tar ((:commit . "fb9ef454a4bb2d6de3415807b4858a20a9cc0dad") (:authors ("Gong Qijian" . "gongqijian@gmail.com")) (:maintainers ("Gong Qijian" . "gongqijian@gmail.com")) (:maintainer "Gong Qijian" . "gongqijian@gmail.com") (:keywords "tools") (:url . "https://github.com/twlz0ne/with-emacs.el"))]) (with-namespace . [(20130407 1822) ((dash (1 1 0)) (loop (1 1))) "interoperable elisp namespaces" tar ((:commit . "36828a40428c8e53c117f2df830b2f7a59ddd306") (:authors ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainers ("Wilfred Hughes" . "me@wilfred.me.uk")) (:maintainer "Wilfred Hughes" . "me@wilfred.me.uk") (:keywords "namespaces"))]) (with-proxy . [(20200510 414) ((emacs (24 4))) "Evaluate expressions with proxy" tar ((:commit . "93b1ed2f3060f305009fa71f4fb5bb10173a10e3") (:authors ("Gong Qijian" . "gongqijian@gmail.com")) (:maintainers ("Gong Qijian" . "gongqijian@gmail.com")) (:maintainer "Gong Qijian" . "gongqijian@gmail.com") (:keywords "comm") (:url . "https://github.com/twlz0ne/with-proxy.el"))]) @@ -5678,7 +5680,7 @@ (yasnippet . [(20240406 1314) ((cl-lib (0 5)) (emacs (24 4))) "Yet another snippet extension for Emacs" tar ((:commit . "eb5ba2664c3a68ae4a53bb38b85418dd131b208f") (:maintainers ("Noam Postavsky" . "npostavs@gmail.com")) (:maintainer "Noam Postavsky" . "npostavs@gmail.com") (:keywords "convenience" "emulation") (:url . "http://github.com/joaotavora/yasnippet"))]) (yasnippet-capf . [(20240420 1531) ((emacs (25 1)) (yasnippet (0 14 0))) "Yasnippet Completion At Point Function" tar ((:commit . "ea2bbf4f1c5c133ca7105a5cda2bc01c8e378ef5") (:authors ("Ellis Kenyő" . "me@elken.dev")) (:maintainers ("Ellis Kenyő" . "me@elken.dev")) (:maintainer "Ellis Kenyő" . "me@elken.dev") (:url . "https://github.com/elken/yasnippet-capf"))]) (yasnippet-lean . [(20220105 2251) ((yasnippet (0 8 0))) "Collection of snippets for the Lean prover" tar ((:commit . "c75485757cc8675ad4f36c1eb028d9d54dc21733") (:maintainers ("Simon Hudon" . "simon.hudon@gmail.com")) (:maintainer "Simon Hudon" . "simon.hudon@gmail.com") (:keywords "convenience" "snippets" "leanprover") (:url . "https://github.com/leanprover-community/yasnippet-lean"))]) - (yasnippet-snippets . [(20240507 943) ((yasnippet (0 8 0))) "Collection of yasnippet snippets" tar ((:commit . "6fafad13bb4689600285d9e38c61958dd63c356d") (:authors ("Andrea Crotti" . "andrea.crotti.0@gmail.com")) (:maintainers ("Andrea Crotti" . "andrea.crotti.0@gmail.com")) (:maintainer "Andrea Crotti" . "andrea.crotti.0@gmail.com") (:keywords "snippets") (:url . "https://github.com/AndreaCrotti/yasnippet-snippets"))]) + (yasnippet-snippets . [(20240603 757) ((yasnippet (0 8 0))) "Collection of yasnippet snippets" tar ((:commit . "1bf034887c4048c38266842686b7f9c8384f72e7") (:authors ("Andrea Crotti" . "andrea.crotti.0@gmail.com")) (:maintainers ("Andrea Crotti" . "andrea.crotti.0@gmail.com")) (:maintainer "Andrea Crotti" . "andrea.crotti.0@gmail.com") (:keywords "snippets") (:url . "https://github.com/AndreaCrotti/yasnippet-snippets"))]) (yatemplate . [(20211115 1208) ((yasnippet (0 8 1)) (emacs (24 3))) "File templates with yasnippet" tar ((:commit . "275745ce1482edc08efb0b7807bc86d832bcc734") (:authors ("Wieland Hoffmann" . "themineo+yatemplate@gmail.com")) (:maintainers ("Wieland Hoffmann" . "themineo+yatemplate@gmail.com")) (:maintainer "Wieland Hoffmann" . "themineo+yatemplate@gmail.com") (:keywords "files" "convenience") (:url . "https://github.com/mineo/yatemplate"))]) (yatex . [(20221225 512) nil "Yet Another tex-mode for emacs //野鳥//" tar ((:commit . "157aa7974191bbb4707d26b05ce830282ad70ef5"))]) (yaxception . [(20240107 504) ((emacs (28)) (dash (2 19 1))) "Provide framework about exception like Java for Elisp" tar ((:commit . "5941de88b19752c14e0dce0d2bf562b1288055a0") (:authors ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainers ("Hiroaki Otsu" . "ootsuhiroaki@gmail.com")) (:maintainer "Hiroaki Otsu" . "ootsuhiroaki@gmail.com") (:keywords "exception" "error" "signal") (:url . "https://github.com/aki2o/yaxception"))]) @@ -5711,6 +5713,7 @@ (zenscript-mode . [(20210102 1350) ((emacs (25 1))) "Major mode for ZenScript" tar ((:commit . "c33b4525502459fe60dd76b383e19919d450aeb8") (:url . "https://github.com/eutropius225/zenscript-mode"))]) (zephir-mode . [(20200417 830) ((cl-lib (0 5)) (pkg-info (0 4)) (emacs (25 1))) "Major mode for editing Zephir code" tar ((:commit . "4e9618b77dff67c1c7b6fff78605a62311db88b8") (:authors ("Serghei Iakovlev" . "egrep@protonmail.ch")) (:maintainers ("Serghei Iakovlev" . "egrep@protonmail.ch")) (:maintainer "Serghei Iakovlev" . "egrep@protonmail.ch") (:keywords "languages") (:url . "https://github.com/zephir-lang/zephir-mode"))]) (zero-input . [(20240527 728) ((emacs (24 4)) (s (1 2 0))) "Zero Chinese input method framework" tar ((:commit . "e87bbf24c1475a784ad9d1ba8447e038824d796b") (:url . "https://gitlab.emacsos.com/sylecn/zero-el"))]) + (zero-input-panel-posframe . [(20240526 1604) ((emacs (24 4)) (zero-input (2 9 0)) (posframe (1 4 3))) "Posframe based zero-input panel implementation" tar ((:commit . "714102090ba87b75a06b87792df696f6f48c2ea8") (:url . "https://gitlab.emacsos.com/sylecn/zero-el"))]) (zerodark-theme . [(20211115 841) ((all-the-icons (2 0 0))) "A dark, medium contrast theme for Emacs" tar ((:commit . "b463528704f6eb00684c0ee003fbd8e42901cde0") (:authors ("Nicolas Petton" . "nicolas@petton.fr")) (:maintainers ("Nicolas Petton" . "nicolas@petton.fr")) (:maintainer "Nicolas Petton" . "nicolas@petton.fr") (:keywords "themes") (:url . "https://github.com/NicolasPetton/zerodark-theme"))]) (zetteldeft . [(20221006 731) ((emacs (25 1)) (deft (0 8)) (ace-window (0 7 0))) "Turn deft into a zettelkasten system" tar ((:commit . "63be6478751376f04d36c6ea52fe65acd69f0927") (:authors ("EFLS <Elias Storms>")) (:maintainers ("EFLS <Elias Storms>")) (:maintainer "EFLS <Elias Storms>") (:keywords "deft" "zettelkasten" "zetteldeft" "wp" "files") (:url . "https://efls.github.io/zetteldeft/"))]) (zetteldesk . [(20230517 2020) ((emacs (27 1)) (org-roam (2 0))) "A revision and outlining tool for org-roam" tar ((:commit . "73f691989c094ec196bb614318ae51b60209a8de") (:authors ("Vidianos Giannitsis" . "vidianosgiannitsis@gmail.com")) (:maintainers ("Vidianos Giannitsis" . "vidianosgiannitsis@gmail.com")) (:maintainer "Vidianos Giannitsis" . "vidianosgiannitsis@gmail.com") (:url . "https://github.com/Vidianos-Giannitsis/zetteldesk.el"))]) diff --git a/emacs/elpa/archives/nongnu/archive-contents b/emacs/elpa/archives/nongnu/archive-contents @@ -341,6 +341,18 @@ (:authors ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:commit . "7980df10e47eef41d4d1c57cfb690ec406381ed3"))]) + (csv2ledger . + [(1 5 4) + ((emacs + (29 1)) + (csv-mode + (1 24))) + "Convert csv files to ledger entries" tar + ((:url . "https://codeberg.org/joostkremers/csv2ledger") + (:maintainer "Joost Kremers" . "joostkremers@fastmail.fm") + (:authors + ("Joost Kremers" . "joostkremers@fastmail.fm")) + (:commit . "5c38ee8a4bd3edd24ff49bcbe63b1b6a659497dc"))]) (cyberpunk-theme . [(1 22) nil "Cyberpunk Color Theme" tar @@ -1905,7 +1917,7 @@ ("David Christiansen" . "david@davidchristiansen.dk")) (:commit . "1edda80e2e32b72e77f4f16ae5b83c312c68ee95"))]) (racket-mode . - [(1 0 20240514 112412) + [(1 0 20240607 81112) ((emacs (25 1))) "Racket editing, REPL, and more" tar @@ -2487,14 +2499,14 @@ ((:url . "https://github.com/lewang/ws-butler") (:commit . "323b651dd70ee40a25accc940b8f80c3a3185205"))]) (xah-fly-keys . - [(25 6 20240523134425) + [(25 8 20240608142416) ((emacs (27))) "ergonomic modal keybinding minor mode." tar ((:url . "http://xahlee.info/emacs/misc/xah-fly-keys.html") (:keywords "convenience" "vi" "vim" "ergoemacs" "keybinding") (:maintainer "Xah Lee" . "xah@xahlee.org") - (:commit . "470c612021ad5370312feefeed48fdcb30088145"))]) + (:commit . "9ca43eff25e1c4b7114f27241d429a2aa3c0c5ba"))]) (xkcd . [(1 1) ((json diff --git a/emacs/elpa/archives/nongnu/archive-contents.signed b/emacs/elpa/archives/nongnu/archive-contents.signed @@ -1 +1 @@ -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) <elpasign@elpa.gnu.org> (trust undefined) created at 2024-06-02T09:05:05+0000 using EDDSA -\ No newline at end of file +Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) <elpasign@elpa.gnu.org> (trust undefined) created at 2024-06-09T09:05:06+0000 using EDDSA +\ No newline at end of file diff --git a/emacs/elpa/doom-modeline-20240510.144/doom-modeline-pkg.el b/emacs/elpa/doom-modeline-20240510.144/doom-modeline-pkg.el @@ -1,17 +0,0 @@ -(define-package "doom-modeline" "20240510.144" "A minimal and modern mode-line" - '((emacs "25.1") - (compat "29.1.4.5") - (nerd-icons "0.1.0") - (shrink-path "0.3.1")) - :commit "65d0bd83eb7c393092e032c24b882f3ba19b4899" :authors - '(("Vincent Zhang" . "seagle0128@gmail.com")) - :maintainers - '(("Vincent Zhang" . "seagle0128@gmail.com")) - :maintainer - '("Vincent Zhang" . "seagle0128@gmail.com") - :keywords - '("faces" "mode-line") - :url "https://github.com/seagle0128/doom-modeline") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/doom-modeline-20240510.144/doom-modeline-segments.el b/emacs/elpa/doom-modeline-20240510.144/doom-modeline-segments.el @@ -1,3218 +0,0 @@ -;;; doom-modeline-segments.el --- The segments for doom-modeline -*- lexical-binding: t; -*- - -;; Copyright (C) 2018-2024 Vincent Zhang - -;; This file is not part of GNU Emacs. - -;; -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. -;; -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see <https://www.gnu.org/licenses/>. -;; - -;;; Commentary: -;; -;; The segments for doom-modeline. -;; Use `doom-modeline-def-segment' to create a new segment. -;; - -;;; Code: - -(require 'doom-modeline-core) -(require 'doom-modeline-env) -(eval-when-compile - (require 'cl-lib) - (require 'seq) - (require 'subr-x)) - - -;; -;; Externals -;; - -(defvar Info-current-file) -(defvar Info-current-node) -(defvar Info-mode-line-node-keymap) -(defvar anzu--cached-count) -(defvar anzu--current-position) -(defvar anzu--overflow-p) -(defvar anzu--state) -(defvar anzu--total-matched) -(defvar anzu-cons-mode-line-p) -(defvar aw-keys) -(defvar battery-echo-area-format) -(defvar battery-load-critical) -(defvar battery-mode-line-format) -(defvar battery-mode-line-limit) -(defvar battery-status-function) -(defvar boon-command-state) -(defvar boon-insert-state) -(defvar boon-off-state) -(defvar boon-special-state) -(defvar display-time-string) -(defvar edebug-execution-mode) -(defvar eglot--managed-mode) -(defvar erc-modified-channels-alist) -(defvar evil-ex-active-highlights-alist) -(defvar evil-ex-argument) -(defvar evil-ex-range) -(defvar evil-mc-frozen) -(defvar evil-state) -(defvar evil-visual-beginning) -(defvar evil-visual-end) -(defvar evil-visual-selection) -(defvar flycheck--automatically-enabled-checkers) -(defvar flycheck-current-errors) -(defvar flycheck-mode-menu-map) -(defvar flymake--mode-line-format) -(defvar flymake--state) -(defvar flymake-menu) -(defvar gnus-newsrc-alist) -(defvar gnus-newsrc-hashtb) -(defvar grip--process) -(defvar helm--mode-line-display-prefarg) -(defvar iedit-occurrences-overlays) -(defvar kele-menu-map) -(defvar meow--indicator) -(defvar minions-mode-line-lighter) -(defvar minions-mode-line-minor-modes-map) -(defvar mlscroll-minimum-current-width) -(defvar mlscroll-right-align) -(defvar mu4e--modeline-item) -(defvar mu4e-alert-mode-line) -(defvar mu4e-alert-modeline-formatter) -(defvar mu4e-modeline-mode) -(defvar nyan-minimum-window-width) -(defvar objed--obj-state) -(defvar objed--object) -(defvar objed-modeline-setup-func) -(defvar persp-nil-name) -(defvar phi-replace--mode-line-format) -(defvar phi-search--overlays) -(defvar phi-search--selection) -(defvar phi-search-mode-line-format) -(defvar poke-line-minimum-window-width) -(defvar rcirc-activity) -(defvar sml-modeline-len) -(defvar symbol-overlay-keywords-alist) -(defvar symbol-overlay-temp-symbol) -(defvar text-scale-mode-amount) -(defvar tracking-buffers) -(defvar winum-auto-setup-mode-line) -(defvar xah-fly-insert-state-p) - -(declare-function anzu--reset-status "ext:anzu") -(declare-function anzu--where-is-here "ext:anzu") -(declare-function async-inject-variables "ext:async") -(declare-function async-start "ext:async") -(declare-function avy-traverse "ext:avy") -(declare-function avy-tree "ext:avy") -(declare-function aw-update "ext:ace-window") -(declare-function aw-window-list "ext:ace-window") -(declare-function battery-format "battery") -(declare-function battery-update "battery") -(declare-function boon-modeline-string "ext:boon") -(declare-function boon-state-string "ext:boon") -(declare-function cider--connection-info "ext:cider") -(declare-function cider-connected-p "ext:cider") -(declare-function cider-current-repl "ext:cider") -(declare-function cider-jack-in "ext:cider") -(declare-function cider-quit "ext:cider") -(declare-function citre-mode "ext:citre-basic-tools") -(declare-function compilation-goto-in-progress-buffer "compile") -(declare-function dap--cur-session "ext:dap-mode") -(declare-function dap--debug-session-name "ext:dap-mode") -(declare-function dap--debug-session-state "ext:dap-mode") -(declare-function dap--session-running "ext:dap-mode") -(declare-function dap-debug-recent "ext:dap-mode") -(declare-function dap-disconnect "ext:dap-mode") -(declare-function dap-hydra "ext:dap-hydra") -(declare-function edebug-help "edebug") -(declare-function edebug-next-mode "edebug") -(declare-function edebug-stop "edebug") -(declare-function eglot "ext:eglot") -(declare-function eglot--major-modes "ext:eglot" t t) -(declare-function eglot--project-nickname "ext:eglot" t t) -(declare-function eglot-clear-status "ext:eglot") -(declare-function eglot-current-server "ext:eglot") -(declare-function eglot-events-buffer "ext:eglot") -(declare-function eglot-forget-pending-continuations "ext:eglot") -(declare-function eglot-managed-p "ext:glot") -(declare-function eglot-reconnect "ext:eglot") -(declare-function eglot-shutdown "ext:eglot") -(declare-function eglot-stderr-buffer "ext:eglot") -(declare-function erc-switch-to-buffer "erc") -(declare-function erc-track-switch-buffer "erc-track") -(declare-function evil-delimited-arguments "ext:evil-common") -(declare-function evil-emacs-state-p "ext:evil-states" t t) -(declare-function evil-force-normal-state "ext:evil-commands" t t) -(declare-function evil-insert-state-p "ext:evil-states" t t) -(declare-function evil-motion-state-p "ext:evil-states" t t) -(declare-function evil-normal-state-p "ext:evil-states" t t) -(declare-function evil-operator-state-p "ext:evil-states" t t) -(declare-function evil-replace-state-p "ext:evil-states" t t) -(declare-function evil-state-property "ext:evil-common") -(declare-function evil-visual-state-p "ext:evil-states" t t) -(declare-function eyebrowse--get "ext:eyebrowse") -(declare-function face-remap-remove-relative "face-remap") -(declare-function fancy-narrow-active-p "ext:fancy-narrow") -(declare-function flycheck-buffer "ext:flycheck") -(declare-function flycheck-count-errors "ext:flycheck") -(declare-function flycheck-error-level-compilation-level "ext:flycheck") -(declare-function flycheck-list-errors "ext:flycheck") -(declare-function flycheck-next-error "ext:flycheck") -(declare-function flycheck-previous-error "ext:flycheck") -(declare-function flymake--diag-type "ext:flymake" t t) -(declare-function flymake--handle-report "ext:flymake") -(declare-function flymake--lookup-type-property "ext:flymake") -(declare-function flymake--state-diags "ext:flymake" t t) -(declare-function flymake-disabled-backends "ext:flymake") -(declare-function flymake-goto-next-error "ext:flymake") -(declare-function flymake-goto-prev-error "ext:flymake") -(declare-function flymake-reporting-backends "ext:flymake") -(declare-function flymake-running-backends "ext:flymake") -(declare-function flymake-show-buffer-diagnostics "ext:flymake") -(declare-function flymake-show-buffer-diagnostics "ext:flymake") -(declare-function flymake-start "ext:flymake") -(declare-function follow-all-followers "follow") -(declare-function gnus-demon-add-handler "gnus-demon") -(declare-function grip--preview-url "ext:grip-mode") -(declare-function grip-browse-preview "ext:grip-mode") -(declare-function grip-restart-preview "ext:grip-mode") -(declare-function grip-stop-preview "ext:grip-mode") -(declare-function helm-candidate-number-at-point "ext:helm-core") -(declare-function helm-get-candidate-number "ext:helm-core") -(declare-function iedit-find-current-occurrence-overlay "ext:iedit-lib") -(declare-function iedit-prev-occurrence "ext:iedit-lib") -(declare-function image-get-display-property "image-mode") -(declare-function jsonrpc--request-continuations "ext:jsonrpc" t t) -(declare-function jsonrpc-last-error "ext:jsonrpc" t t) -(declare-function kele-current-context-name "ext:kele") -(declare-function kele-current-namespace "ext:kele") -(declare-function lsp--workspace-print "ext:lsp-mode") -(declare-function lsp-describe-session "ext:lsp-mode") -(declare-function lsp-workspace-folders-open "ext:lsp-mode") -(declare-function lsp-workspace-restart "ext:lsp-mode") -(declare-function lsp-workspace-shutdown "ext:lsp-mode") -(declare-function lsp-workspaces "ext:lsp-mode") -(declare-function lv-message "ext:lv") -(declare-function mc/num-cursors "ext:multiple-cursors-core") -(declare-function minions--prominent-modes "ext:minions") -(declare-function mlscroll-mode-line "ext:mlscroll") -(declare-function mu4e--modeline-string "ext:mu4e-modeline") -(declare-function mu4e-alert-default-mode-line-formatter "ext:mu4e-alert") -(declare-function mu4e-alert-enable-mode-line-display "ext:mu4e-alert") -(declare-function nyan-create "ext:nyan-mode") -(declare-function org-edit-src-save "ext:org-src") -(declare-function parrot-create "ext:parrot") -(declare-function pdf-cache-number-of-pages "ext:pdf-cache" t t) -(declare-function persp-add-buffer "ext:persp-mode") -(declare-function persp-contain-buffer-p "ext:persp-mode") -(declare-function persp-switch "ext:persp-mode") -(declare-function phi-search--initialize "ext:phi-search") -(declare-function poke-line-create "ext:poke-line") -(declare-function popup-create "ext:popup") -(declare-function popup-delete "ext:popup") -(declare-function rcirc-next-active-buffer "rcirc") -(declare-function rcirc-short-buffer-name "rcirc") -(declare-function rcirc-switch-to-server-buffer "rcirc") -(declare-function rcirc-window-configuration-change "rcirc") -(declare-function rime--should-enable-p "ext:rime") -(declare-function rime--should-inline-ascii-p "ext:rime") -(declare-function sml-modeline-create "ext:sml-modeline") -(declare-function svg-circle "svg") -(declare-function svg-create "svg") -(declare-function svg-image "svg") -(declare-function svg-line "svg") -(declare-function symbol-overlay-assoc "ext:symbol-overlay") -(declare-function symbol-overlay-get-list "ext:symbol-overlay") -(declare-function symbol-overlay-get-symbol "ext:symbol-overlay") -(declare-function symbol-overlay-rename "ext:symbol-overlay") -(declare-function tab-bar--current-tab "tab-bar") -(declare-function tab-bar--current-tab-index "tab-bar") -(declare-function tracking-next-buffer "ext:tracking") -(declare-function tracking-previous-buffer "ext:tracking") -(declare-function tracking-shorten "ext:tracking") -(declare-function undo-tree-redo-1 "ext:undo-tree") -(declare-function undo-tree-undo-1 "ext:undo-tree") -(declare-function warning-numeric-level "warnings") -(declare-function window-numbering-clear-mode-line "ext:window-numbering") -(declare-function window-numbering-get-number-string "ext:window-numbering") -(declare-function window-numbering-install-mode-line "ext:window-numbering") -(declare-function winum--clear-mode-line "ext:winum") -(declare-function winum--install-mode-line "ext:winum") -(declare-function winum-get-number-string "ext:winum") - - - -;; -;; Buffer information -;; - -(defvar-local doom-modeline--buffer-file-icon nil) -(defun doom-modeline-update-buffer-file-icon (&rest _) - "Update file icon in mode-line." - (setq doom-modeline--buffer-file-icon - (when (and doom-modeline-major-mode-icon - (doom-modeline-icon-displayable-p)) - (let ((icon (doom-modeline-icon-for-buffer))) - (propertize (if (or (null icon) (symbolp icon)) - (doom-modeline-icon 'faicon "nf-fa-file_o" nil nil - :face 'nerd-icons-dsilver) - (doom-modeline-propertize-icon icon)) - 'help-echo (format "Major-mode: %s" (format-mode-line mode-name))))))) -(add-hook 'find-file-hook #'doom-modeline-update-buffer-file-icon) -(add-hook 'after-change-major-mode-hook #'doom-modeline-update-buffer-file-icon) -(add-hook 'clone-indirect-buffer-hook #'doom-modeline-update-buffer-file-icon) - -(doom-modeline-add-variable-watcher - 'doom-modeline-icon - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-icon val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (doom-modeline-update-buffer-file-icon)))))) - -(defun doom-modeline-buffer-file-state-icon (icon unicode text face) - "Displays an ICON of buffer state with FACE. -UNICODE and TEXT are the alternatives if it is not applicable. -Uses `nerd-icons-mdicon' to fetch the icon." - (doom-modeline-icon 'mdicon icon unicode text :face face)) - -(defvar-local doom-modeline--buffer-file-state-icon nil) -(defun doom-modeline-update-buffer-file-state-icon (&rest _) - "Update the buffer or file state in mode-line." - (setq doom-modeline--buffer-file-state-icon - (when doom-modeline-buffer-state-icon - (ignore-errors - (concat - (cond (buffer-read-only - (doom-modeline-buffer-file-state-icon - "nf-md-lock" "🔒" "%1*" - 'doom-modeline-warning)) - ((and buffer-file-name (buffer-modified-p) - doom-modeline-buffer-modification-icon) - (doom-modeline-buffer-file-state-icon - "nf-md-content_save_edit" "💾" "%1*" - 'doom-modeline-warning)) - ((and buffer-file-name - ;; Avoid freezing while connection is lost - (not (file-remote-p buffer-file-name)) - (not (file-exists-p buffer-file-name))) - (doom-modeline-buffer-file-state-icon - "nf-md-cancel" "🚫" "!" - 'doom-modeline-urgent)) - (t "")) - (when (or (buffer-narrowed-p) - (and (bound-and-true-p fancy-narrow-mode) - (fancy-narrow-active-p)) - (bound-and-true-p dired-narrow-mode)) - (doom-modeline-buffer-file-state-icon - "nf-md-unfold_less_horizontal" "↕" "><" - 'doom-modeline-warning))))))) - -(defvar-local doom-modeline--buffer-file-name nil) -(defun doom-modeline-update-buffer-file-name (&rest _) - "Update buffer file name in mode-line." - (setq doom-modeline--buffer-file-name - (ignore-errors - (save-match-data - (if buffer-file-name - (doom-modeline-buffer-file-name) - (propertize "%b" - 'face 'doom-modeline-buffer-file - 'mouse-face 'doom-modeline-highlight - 'help-echo "Buffer name -mouse-1: Previous buffer\nmouse-3: Next buffer" - 'local-map mode-line-buffer-identification-keymap)))))) -(add-hook 'find-file-hook #'doom-modeline-update-buffer-file-name) -(add-hook 'after-save-hook #'doom-modeline-update-buffer-file-name) -(add-hook 'clone-indirect-buffer-hook #'doom-modeline-update-buffer-file-name) -(add-hook 'evil-insert-state-exit-hook #'doom-modeline-update-buffer-file-name) -(add-hook 'Info-selection-hook #'doom-modeline-update-buffer-file-name) -(advice-add #'rename-buffer :after #'doom-modeline-update-buffer-file-name) -(advice-add #'set-visited-file-name :after #'doom-modeline-update-buffer-file-name) -(advice-add #'pop-to-buffer :after #'doom-modeline-update-buffer-file-name) -(advice-add #'popup-create :after #'doom-modeline-update-buffer-file-name) -(advice-add #'popup-delete :after #'doom-modeline-update-buffer-file-name) -;; (advice-add #'primitive-undo :after #'doom-modeline-update-buffer-file-name) -;; (advice-add #'set-buffer-modified-p :after #'doom-modeline-update-buffer-file-name) - -(with-no-warnings - (if (boundp 'after-focus-change-function) - (progn - (advice-add #'handle-switch-frame :after #'doom-modeline-update-buffer-file-name) - (add-function :after after-focus-change-function #'doom-modeline-update-buffer-file-name)) - (progn - (add-hook 'focus-in-hook #'doom-modeline-update-buffer-file-name) - (add-hook 'focus-out-hook #'doom-modeline-update-buffer-file-name)))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-buffer-file-name-style - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-buffer-file-name-style val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when buffer-file-name - (doom-modeline-update-buffer-file-name))))))) - -(defsubst doom-modeline--buffer-mode-icon () - "The icon of the current major mode." - (when (and doom-modeline-icon doom-modeline-major-mode-icon) - (when-let ((icon (or doom-modeline--buffer-file-icon - (doom-modeline-update-buffer-file-icon)))) - (unless (string-empty-p icon) - (concat - (if doom-modeline-major-mode-color-icon - (doom-modeline-display-icon icon) - (doom-modeline-propertize-icon - icon - (doom-modeline-face))) - (doom-modeline-vspc)))))) - -(defsubst doom-modeline--buffer-state-icon () - "The icon of the current buffer state." - (when doom-modeline-buffer-state-icon - (when-let ((icon (doom-modeline-update-buffer-file-state-icon))) - (unless (string-empty-p icon) - (concat - (doom-modeline-display-icon icon) - (doom-modeline-vspc)))))) - -(defsubst doom-modeline--buffer-simple-name () - "The buffer simple name." - (propertize "%b" - 'face (doom-modeline-face - (if (and doom-modeline-highlight-modified-buffer-name - (buffer-modified-p)) - 'doom-modeline-buffer-modified - 'doom-modeline-buffer-file)) - 'mouse-face 'doom-modeline-highlight - 'help-echo "Buffer name -mouse-1: Previous buffer\nmouse-3: Next buffer" - 'local-map mode-line-buffer-identification-keymap)) - -(defsubst doom-modeline--buffer-name () - "The current buffer name." - (when doom-modeline-buffer-name - (if (and (not (eq doom-modeline-buffer-file-name-style 'file-name)) - doom-modeline--limited-width-p) - ;; Only display the buffer name if the window is small, and doesn't - ;; need to respect file-name style. - (doom-modeline--buffer-simple-name) - (when-let ((name (or doom-modeline--buffer-file-name - (doom-modeline-update-buffer-file-name)))) - ;; Check if the buffer is modified - (if (and doom-modeline-highlight-modified-buffer-name - (buffer-modified-p)) - (propertize name 'face (doom-modeline-face 'doom-modeline-buffer-modified)) - (doom-modeline-display-text name)))))) - -(doom-modeline-def-segment buffer-info - "Combined information about the current buffer. - -Including the current working directory, the file name, and its state (modified, -read-only or non-existent)." - (concat - (doom-modeline-spc) - (doom-modeline--buffer-mode-icon) - (doom-modeline--buffer-state-icon) - (doom-modeline--buffer-name))) - -(doom-modeline-def-segment buffer-info-simple - "Display only the current buffer's name, but with fontification." - (concat - (doom-modeline-spc) - (doom-modeline--buffer-mode-icon) - (doom-modeline--buffer-state-icon) - (doom-modeline--buffer-simple-name))) - -(doom-modeline-def-segment calc - "Display calculator icons and info." - (concat - (doom-modeline-spc) - (when-let ((icon (doom-modeline-icon 'faicon "nf-fa-calculator" "🖩" ""))) - (concat - (doom-modeline-display-icon icon) - (doom-modeline-vspc))) - (doom-modeline--buffer-simple-name))) - -(doom-modeline-def-segment buffer-default-directory - "Displays `default-directory' with the icon and state. - -This is for special buffers like the scratch buffer where knowing the current -project directory is important." - (let ((face (doom-modeline-face - (if (and buffer-file-name (buffer-modified-p)) - 'doom-modeline-buffer-modified - 'doom-modeline-buffer-path)))) - (concat - (doom-modeline-spc) - (and doom-modeline-major-mode-icon - (concat - (doom-modeline-icon - 'octicon "nf-oct-file_directory_fill" "🖿" "" :face face) - (doom-modeline-vspc))) - (doom-modeline--buffer-state-icon) - (propertize (abbreviate-file-name default-directory) 'face face)))) - -(doom-modeline-def-segment buffer-default-directory-simple - "Displays `default-directory'. - -This is for special buffers like the scratch buffer where knowing the current -project directory is important." - (let ((face (doom-modeline-face 'doom-modeline-buffer-path))) - (concat - (doom-modeline-spc) - (and doom-modeline-major-mode-icon - (concat - (doom-modeline-icon - 'octicon "nf-oct-file_directory_fill" "🖿" "" :face face) - (doom-modeline-vspc))) - (propertize (abbreviate-file-name default-directory) 'face face)))) - - -;; -;; Encoding -;; - -(doom-modeline-def-segment buffer-encoding - "Displays the eol and the encoding style of the buffer." - (when doom-modeline-buffer-encoding - (let ((sep (doom-modeline-spc)) - (face (doom-modeline-face)) - (mouse-face 'doom-modeline-highlight)) - (concat - sep - - ;; eol type - (let ((eol (coding-system-eol-type buffer-file-coding-system))) - (when (or (eq doom-modeline-buffer-encoding t) - (and (eq doom-modeline-buffer-encoding 'nondefault) - (not (equal eol doom-modeline-default-eol-type)))) - (propertize - (pcase eol - (0 "LF ") - (1 "CRLF ") - (2 "CR ") - (_ "")) - 'face face - 'mouse-face mouse-face - 'help-echo (format "End-of-line style: %s\nmouse-1: Cycle" - (pcase eol - (0 "Unix-style LF") - (1 "DOS-style CRLF") - (2 "Mac-style CR") - (_ "Undecided"))) - 'local-map (let ((map (make-sparse-keymap))) - (define-key map [mode-line mouse-1] 'mode-line-change-eol) - map)))) - - ;; coding system - (let* ((sys (coding-system-plist buffer-file-coding-system)) - (cat (plist-get sys :category)) - (sym (if (memq cat - '(coding-category-undecided coding-category-utf-8)) - 'utf-8 - (plist-get sys :name)))) - (when (or (eq doom-modeline-buffer-encoding t) - (and (eq doom-modeline-buffer-encoding 'nondefault) - (not (eq cat 'coding-category-undecided)) - (not (eq sym doom-modeline-default-coding-system)))) - (propertize - (upcase (symbol-name sym)) - 'face face - 'mouse-face mouse-face - 'help-echo 'mode-line-mule-info-help-echo - 'local-map mode-line-coding-system-map))) - - sep)))) - - -;; -;; Indentation -;; - -(doom-modeline-def-segment indent-info - "Displays the indentation information." - (when doom-modeline-indent-info - (let ((do-propertize - (lambda (mode size) - (propertize - (format " %s %d " mode size) - 'face (doom-modeline-face))))) - (if indent-tabs-mode - (funcall do-propertize "TAB" tab-width) - (let ((lookup-var - (seq-find (lambda (var) - (and var (boundp var) (symbol-value var))) - (cdr (assoc major-mode doom-modeline-indent-alist)) nil))) - (funcall do-propertize "SPC" - (if lookup-var - (symbol-value lookup-var) - tab-width))))))) - -;; -;; Remote host -;; - -(doom-modeline-def-segment remote-host - "Hostname for remote buffers." - (when default-directory - (when-let ((host (file-remote-p default-directory 'host))) - (propertize - (concat "@" host) - 'face (doom-modeline-face 'doom-modeline-host))))) - - -;; -;; Major mode -;; - -(doom-modeline-def-segment major-mode - "The major mode, including environment and text-scale info." - (let ((sep (doom-modeline-spc)) - (face (doom-modeline-face 'doom-modeline-buffer-major-mode))) - (concat - sep - (propertize (concat - (format-mode-line - (or (and (boundp 'delighted-modes) - (cadr (assq major-mode delighted-modes))) - mode-name)) - (when (and doom-modeline-env-version doom-modeline-env--version) - (format " %s" doom-modeline-env--version))) - 'help-echo "Major mode\n\ -mouse-1: Display major mode menu\n\ -mouse-2: Show help for major mode\n\ -mouse-3: Toggle minor modes" - 'face face - 'mouse-face 'doom-modeline-highlight - 'local-map mode-line-major-mode-keymap) - (and (boundp 'text-scale-mode-amount) - (/= text-scale-mode-amount 0) - (propertize - (format - (if (> text-scale-mode-amount 0) " (%+d)" " (%-d)") - text-scale-mode-amount) - 'face face)) - sep))) - - -;; -;; Process -;; - -(doom-modeline-def-segment process - "The process info." - (doom-modeline-display-text - (format-mode-line mode-line-process))) - - -;; -;; Minor modes -;; - -(doom-modeline-def-segment minor-modes - (when doom-modeline-minor-modes - (let ((sep (doom-modeline-spc)) - (face (doom-modeline-face 'doom-modeline-buffer-minor-mode)) - (mouse-face 'doom-modeline-highlight) - (help-echo "Minor mode - mouse-1: Display minor mode menu - mouse-2: Show help for minor mode - mouse-3: Toggle minor modes")) - (if (bound-and-true-p minions-mode) - `((:propertize ("" ,(minions--prominent-modes)) - face ,face - mouse-face ,mouse-face - help-echo ,help-echo - local-map ,mode-line-minor-mode-keymap) - ,sep - (:propertize ("" ,(doom-modeline-icon 'octicon "nf-oct-gear" "⚙" - minions-mode-line-lighter - :face face)) - mouse-face ,mouse-face - help-echo "Minions -mouse-1: Display minor modes menu" - local-map ,minions-mode-line-minor-modes-map) - ,sep) - `((:propertize ("" minor-mode-alist) - face ,face - mouse-face ,mouse-face - help-echo ,help-echo - local-map ,mode-line-minor-mode-keymap) - ,sep))))) - - -;; -;; VCS -;; - -(defun doom-modeline-vcs-icon (icon &optional unicode text face) - "Displays the vcs ICON with FACE and VOFFSET. - -UNICODE and TEXT are fallbacks. -Uses `nerd-icons-octicon' to fetch the icon." - (doom-modeline-icon 'devicon (and doom-modeline-vcs-icon icon) - unicode text :face face)) - -(defvar-local doom-modeline--vcs nil) -(defun doom-modeline-update-vcs (&rest _) - "Update vcs state in mode-line." - (setq doom-modeline--vcs - (when (and vc-mode buffer-file-name) - (let* ((backend (vc-backend buffer-file-name)) - (state (vc-state buffer-file-name backend)) - (icon (cond ((memq state '(edited added)) - (doom-modeline-vcs-icon "nf-dev-git_compare" "🔃" "*" 'doom-modeline-info)) - ((eq state 'needs-merge) - (doom-modeline-vcs-icon "nf-dev-git_merge" "🔀" "?" 'doom-modeline-info)) - ((eq state 'needs-update) - (doom-modeline-vcs-icon "nf-dev-git_pull_request" "⬇" "!" 'doom-modeline-warning)) - ((memq state '(removed conflict unregistered)) - (doom-modeline-icon 'octicon "nf-oct-alert" "⚠" "!" :face 'doom-modeline-urgent)) - (t (doom-modeline-vcs-icon "nf-dev-git_branch" "" "@" 'doom-modeline-info)))) - (str (if vc-display-status - (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2)) - "")) - (face (cond ((eq state 'needs-update) - '(doom-modeline-warning bold)) - ((memq state '(removed conflict unregistered)) - '(doom-modeline-urgent bold)) - (t '(doom-modeline-info bold)))) - (text (propertize (if (length> str doom-modeline-vcs-max-length) - (concat - (substring str 0 (- doom-modeline-vcs-max-length 3)) - doom-modeline-ellipsis) - str) - 'face face))) - `((icon . ,icon) (text . ,text)))))) -(add-hook 'find-file-hook #'doom-modeline-update-vcs) -(add-hook 'after-save-hook #'doom-modeline-update-vcs) -(advice-add #'vc-refresh-state :after #'doom-modeline-update-vcs) - -(doom-modeline-add-variable-watcher - 'doom-modeline-icon - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-icon val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (doom-modeline-update-vcs)))))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-unicode-fallback - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-unicode-fallback val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (doom-modeline-update-vcs)))))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-vcs-icon - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-vcs-icon val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (doom-modeline-update-vcs)))))) - -(doom-modeline-def-segment vcs - "Displays the current branch, colored based on its state." - (when doom-modeline--vcs - (let-alist doom-modeline--vcs - (let ((sep (doom-modeline-spc)) - (vsep (doom-modeline-vspc))) - (concat sep - (propertize (concat - (doom-modeline-display-icon .icon) - vsep - (doom-modeline-display-text .text)) - 'help-echo (get-text-property 1 'help-echo vc-mode) - 'mouse-face 'doom-modeline-highlight - 'local-map (get-text-property 1 'local-map vc-mode)) - sep))))) - - -;; -;; Check -;; - -(defun doom-modeline-check-icon (icon unicode text face) - "Displays the check ICON with FACE. - -UNICODE and TEXT are fallbacks. -Uses `nerd-icons-mdicon' to fetch the icon." - (doom-modeline-icon 'mdicon (and doom-modeline-check-icon icon) - unicode text :face face)) - -(defun doom-modeline-check-text (text &optional face) - "Displays the check TEXT with FACE." - (propertize text 'face (or face 'mode-line))) - -;; Flycheck - -(defun doom-modeline--flycheck-count-errors () - "Count the number of ERRORS, grouped by level. - -Return an alist, where each ITEM is a cons cell whose `car' is an -error level, and whose `cdr' is the number of errors of that -level." - (let ((info 0) (warning 0) (error 0)) - (mapc - (lambda (item) - (let ((count (cdr item))) - (pcase (flycheck-error-level-compilation-level (car item)) - (0 (cl-incf info count)) - (1 (cl-incf warning count)) - (2 (cl-incf error count))))) - (flycheck-count-errors flycheck-current-errors)) - `((info . ,info) (warning . ,warning) (error . ,error)))) - -(defvar-local doom-modeline--flycheck nil) -(defun doom-modeline-update-flycheck (&optional status) - "Update flycheck via STATUS." - (setq doom-modeline--flycheck - (let-alist (doom-modeline--flycheck-count-errors) - (let* ((vsep (doom-modeline-vspc)) - (seg (if doom-modeline-check-simple-format - (let ((count (+ .error .warning .info))) - (pcase status - ('finished (if (> count 0) - (let ((face (if (> .error 0) 'doom-modeline-urgent 'doom-modeline-warning))) - (concat - (doom-modeline-check-icon "nf-md-alert_circle_outline" "⚠" "!" face) - vsep - (doom-modeline-check-text (number-to-string count) face))) - (doom-modeline-check-icon "nf-md-check_circle_outline" "✔" "" 'doom-modeline-info))) - ('running (concat - (doom-modeline-check-icon "nf-md-timer_sand" "⏳" "*" 'doom-modeline-debug) - (when (> count 0) - (concat - vsep - (doom-modeline-check-text (number-to-string count) 'doom-modeline-debug))))) - ('no-checker (doom-modeline-check-icon "nf-md-alert_box_outline" "⚠" "-" 'doom-modeline-debug)) - ('errored (doom-modeline-check-icon "nf-md-alert_box_outline" "⚠" "!" 'doom-modeline-urgent)) - ('interrupted (doom-modeline-check-icon "nf-md-pause_circle_outline" "⦷" "." 'doom-modeline-debug)) - ('suspicious (doom-modeline-check-icon "nf-md-file_question_outline" "❓" "?" 'doom-modeline-debug)) - (_ ""))) - (concat (doom-modeline-check-icon "nf-md-close_circle_outline" "⮾" "!" 'doom-modeline-urgent) - vsep - (doom-modeline-check-text (number-to-string .error) 'doom-modeline-urgent) - vsep - (doom-modeline-check-icon "nf-md-alert_outline" "⚠" "!" 'doom-modeline-warning) - vsep - (doom-modeline-check-text (number-to-string .warning) 'doom-modeline-warning) - vsep - (doom-modeline-check-icon "nf-md-information_outline" "🛈" "!" 'doom-modeline-info) - vsep - (doom-modeline-check-text (number-to-string .info) 'doom-modeline-info))))) - (propertize seg - 'help-echo (concat "Flycheck\n" - (pcase status - ('finished (format "error: %d, warning: %d, info: %d" .error .warning .info)) - ('running "Checking...") - ('no-checker "No Checker") - ('errored "Error") - ('interrupted "Interrupted") - ('suspicious "Suspicious")) - "\nmouse-1: Display minor mode menu\nmouse-2: Show help for minor mode") - 'mouse-face 'doom-modeline-highlight - 'local-map (let ((map (make-sparse-keymap))) - (define-key map [mode-line down-mouse-1] - flycheck-mode-menu-map) - (define-key map [mode-line mouse-2] - (lambda () - (interactive) - (describe-function 'flycheck-mode))) - map)))))) -(add-hook 'flycheck-status-changed-functions #'doom-modeline-update-flycheck) -(add-hook 'flycheck-mode-hook #'doom-modeline-update-flycheck) - -(doom-modeline-add-variable-watcher - 'doom-modeline-icon - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-icon val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (bound-and-true-p flycheck-mode) - (doom-modeline-update-flycheck))))))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-check-icon - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-check-icon val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (bound-and-true-p flycheck-mode) - (doom-modeline-update-flycheck))))))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-unicode-fallback - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-unicode-fallback val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (bound-and-true-p flycheck-mode) - (doom-modeline-update-flycheck))))))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-check-simple-format - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-check-simple-format val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (bound-and-true-p flycheck-mode) - (doom-modeline-update-flycheck))))))) - -;; Flymake - -;; Compatibility -;; @see https://github.com/emacs-mirror/emacs/commit/6e100869012da9244679696634cab6b9cac96303. -(with-eval-after-load 'flymake - (unless (boundp 'flymake--state) - (defvaralias 'flymake--state 'flymake--backend-state)) - (unless (fboundp 'flymake--state-diags) - (defalias 'flymake--state-diags 'flymake--backend-state-diags))) - -(defun doom-modeline--flymake-count-errors () - "Count the number of ERRORS, grouped by level." - (let ((warning-level (warning-numeric-level :warning)) - (note-level (warning-numeric-level :debug)) - (note 0) (warning 0) (error 0)) - (maphash (lambda (_b state) - (cl-loop - with diags = (flymake--state-diags state) - for diag in diags do - (let ((severity (flymake--lookup-type-property (flymake--diag-type diag) 'severity - (warning-numeric-level :error)))) - (cond ((> severity warning-level) (cl-incf error)) - ((> severity note-level) (cl-incf warning)) - (t (cl-incf note)))))) - flymake--state) - `((note . ,note) (warning . ,warning) (error . ,error)))) - -(defvar-local doom-modeline--flymake nil) -(defun doom-modeline-update-flymake (&rest _) - "Update flymake." - (setq doom-modeline--flymake - (let* ((known (hash-table-keys flymake--state)) - (running (flymake-running-backends)) - (disabled (flymake-disabled-backends)) - (reported (flymake-reporting-backends)) - (all-disabled (and disabled (null running))) - (some-waiting (cl-set-difference running reported))) - (let-alist (doom-modeline--flymake-count-errors) - (let* ((vsep (doom-modeline-vspc)) - (seg (if doom-modeline-check-simple-format - (let ((count (+ .error .warning .note))) - (cond - (some-waiting (concat - (doom-modeline-check-icon "nf-md-timer_sand" "⏳" "*" 'doom-modeline-debug) - (when (> count 0) - (concat - vsep - (doom-modeline-check-text (number-to-string count) 'doom-modeline-debug))))) - ((null known) (doom-modeline-check-icon "nf-md-alert_box_outline" "⚠" "!" 'doom-modeline-urgent)) - (all-disabled (doom-modeline-check-icon "nf-md-alert_box_outline" "⚠" "!" 'doom-modeline-warning)) - (t (if (> count 0) - (let ((face (if (> .error 0) 'doom-modeline-urgent 'doom-modeline-warning))) - (concat - (doom-modeline-check-icon "nf-md-alert_circle_outline" "⚠" "!" face) - vsep - (doom-modeline-check-text (number-to-string count) face))) - (doom-modeline-check-icon "nf-md-check_circle_outline" "✔" "" 'doom-modeline-info))))) - (concat (doom-modeline-check-icon "nf-md-close_circle_outline" "⮾" "!" 'doom-modeline-urgent) - vsep - (doom-modeline-check-text (number-to-string .error) 'doom-modeline-urgent) - vsep - (doom-modeline-check-icon "nf-md-alert_outline" "⚠" "!" 'doom-modeline-warning) - vsep - (doom-modeline-check-text (number-to-string .warning) 'doom-modeline-warning) - vsep - (doom-modeline-check-icon "nf-md-information_outline" "🛈" "!" 'doom-modeline-info) - vsep - (doom-modeline-check-text (number-to-string .note) 'doom-modeline-info))))) - (propertize - seg - 'help-echo (concat "Flymake\n" - (cond - (some-waiting "Checking...") - ((null known) "No Checker") - (all-disabled "All Checkers Disabled") - (t (format "%d/%d backends running\nerror: %d, warning: %d, note: %d" - (length running) (length known) .error .warning .note))) - "\nmouse-1: Display minor mode menu\nmouse-2: Show help for minor mode") - 'mouse-face 'doom-modeline-highlight - 'local-map (let ((map (make-sparse-keymap))) - (define-key map [mode-line down-mouse-1] - flymake-menu) - (define-key map [mode-line mouse-2] - (lambda () - (interactive) - (describe-function 'flymake-mode))) - map))))))) -(advice-add #'flymake--handle-report :after #'doom-modeline-update-flymake) - -(doom-modeline-add-variable-watcher - 'doom-modeline-icon - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-icon val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (bound-and-true-p flymake-mode) - (doom-modeline-update-flymake))))))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-check-icon - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-check-icon val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (bound-and-true-p flymake-mode) - (doom-modeline-update-flymake))))))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-unicode-fallback - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-unicode-fallback val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (bound-and-true-p flymake-mode) - (doom-modeline-update-flymake))))))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-check-simple-format - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-check-simple-format val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (bound-and-true-p flymake-mode) - (doom-modeline-update-flymake))))))) - -(doom-modeline-def-segment check - "Displays color-coded error status in the current buffer with pretty icons." - (when-let ((sep (doom-modeline-spc)) - (vsep (doom-modeline-vspc)) - (seg (cond - ((and (bound-and-true-p flymake-mode) - (bound-and-true-p flymake--state)) ; only support 26+ - doom-modeline--flymake) - ((and (bound-and-true-p flycheck-mode) - (bound-and-true-p flycheck--automatically-enabled-checkers)) - doom-modeline--flycheck)))) - (concat - sep - (let ((str)) - (dolist (s (split-string seg " ")) - (setq str - (concat str - (if (string-match-p "^[0-9]+$" s) - (concat vsep - (doom-modeline-display-text s) - vsep) - (doom-modeline-display-icon s))))) - (propertize str - 'help-echo (get-text-property 1 'help-echo seg) - 'mouse-face 'doom-modeline-highlight - 'local-map (get-text-property 1 'local-map seg))) - sep))) - - -;; -;; Word Count -;; - -(doom-modeline-def-segment word-count - "The buffer word count. -Displayed when in a major mode in `doom-modeline-continuous-word-count-modes'. -Respects `doom-modeline-enable-word-count'." - (when (and doom-modeline-enable-word-count - (member major-mode doom-modeline-continuous-word-count-modes)) - (propertize (format " %dW" (count-words (point-min) (point-max))) - 'face (doom-modeline-face)))) - - -;; -;; Selection -;; - -(defsubst doom-modeline-column (pos) - "Get the column of the position `POS'." - (save-excursion (goto-char pos) - (current-column))) - -(doom-modeline-def-segment selection-info - "Information about the current selection. - -Such as how many characters and lines are selected, or the NxM dimensions of a -block selection." - (when (and (or mark-active (and (bound-and-true-p evil-local-mode) - (eq evil-state 'visual))) - (doom-modeline--active)) - (cl-destructuring-bind (beg . end) - (if (and (bound-and-true-p evil-local-mode) (eq evil-state 'visual)) - (cons evil-visual-beginning evil-visual-end) - (cons (region-beginning) (region-end))) - (propertize - (let ((lines (count-lines beg (min end (point-max))))) - (concat - " " - (cond ((or (bound-and-true-p rectangle-mark-mode) - (and (bound-and-true-p evil-visual-selection) - (eq 'block evil-visual-selection))) - (let ((cols (abs (- (doom-modeline-column end) - (doom-modeline-column beg))))) - (format "%dx%dB" lines cols))) - ((and (bound-and-true-p evil-visual-selection) - (eq evil-visual-selection 'line)) - (format "%dL" lines)) - ((> lines 1) - (format "%dC %dL" (- end beg) lines)) - (t - (format "%dC" (- end beg)))) - (when doom-modeline-enable-word-count - (format " %dW" (count-words beg end))) - " ")) - 'face 'doom-modeline-emphasis)))) - - -;; -;; Matches (macro, anzu, evil-substitute, iedit, symbol-overlay and multi-cursors) -;; - -(defsubst doom-modeline--macro-recording () - "Display current Emacs or evil macro being recorded." - (when (and (doom-modeline--active) - (or defining-kbd-macro executing-kbd-macro)) - (let ((sep (propertize " " 'face 'doom-modeline-panel)) - (vsep (propertize " " 'face - '(:inherit (doom-modeline-panel variable-pitch)))) - (macro-name (if (bound-and-true-p evil-this-macro) - (format " @%s " - (char-to-string evil-this-macro)) - "Macro"))) - (concat - sep - (if doom-modeline-always-show-macro-register - (propertize macro-name 'face 'doom-modeline-panel) - (concat - (doom-modeline-icon 'mdicon "nf-md-record" "●" - macro-name - :face '(:inherit (doom-modeline-urgent doom-modeline-panel)) - :v-adjust 0.15) - vsep - (doom-modeline-icon 'mdicon "nf-md-menu_right" "▶" ">" - :face 'doom-modeline-panel - :v-adjust 0.15))) - sep)))) - -;; `anzu' and `evil-anzu' expose current/total state that can be displayed in the -;; mode-line. -(defun doom-modeline-fix-anzu-count (positions here) - "Calulate anzu count via POSITIONS and HERE." - (cl-loop for (start . end) in positions - collect t into before - when (and (>= here start) (<= here end)) - return (length before) - finally return 0)) - -(advice-add #'anzu--where-is-here :override #'doom-modeline-fix-anzu-count) - -(setq anzu-cons-mode-line-p nil) ; manage modeline segment ourselves -;; Ensure anzu state is cleared when searches & iedit are done -(with-eval-after-load 'anzu - (add-hook 'isearch-mode-end-hook #'anzu--reset-status t) - (add-hook 'iedit-mode-end-hook #'anzu--reset-status) - (advice-add #'evil-force-normal-state :after #'anzu--reset-status) - ;; Fix matches segment mirroring across all buffers - (mapc #'make-variable-buffer-local - '(anzu--total-matched - anzu--current-position anzu--state anzu--cached-count - anzu--cached-positions anzu--last-command - anzu--last-isearch-string anzu--overflow-p))) - -(defsubst doom-modeline--anzu () - "Show the match index and total number thereof. -Requires `anzu', also `evil-anzu' if using `evil-mode' for compatibility with -`evil-search'." - (when (and (bound-and-true-p anzu--state) - (not (bound-and-true-p iedit-mode))) - (propertize - (let ((here anzu--current-position) - (total anzu--total-matched)) - (cond ((eq anzu--state 'replace-query) - (format " %d replace " anzu--cached-count)) - ((eq anzu--state 'replace) - (format " %d/%d " here total)) - (anzu--overflow-p - (format " %s+ " total)) - (t - (format " %s/%d " here total)))) - 'face (doom-modeline-face 'doom-modeline-panel)))) - -(defsubst doom-modeline--evil-substitute () - "Show number of matches for `evil-ex' in real time. -The number of matches contains substitutions and highlightings." - (when (and (bound-and-true-p evil-local-mode) - (or (assq 'evil-ex-substitute evil-ex-active-highlights-alist) - (assq 'evil-ex-global-match evil-ex-active-highlights-alist) - (assq 'evil-ex-buffer-match evil-ex-active-highlights-alist))) - (propertize - (let ((range (if evil-ex-range - (cons (car evil-ex-range) (cadr evil-ex-range)) - (cons (line-beginning-position) (line-end-position)))) - (pattern (car-safe (evil-delimited-arguments evil-ex-argument 2)))) - (if pattern - (format " %s matches " (how-many pattern (car range) (cdr range))) - " - ")) - 'face (doom-modeline-face 'doom-modeline-panel)))) - -(defun doom-modeline-themes--overlay-sort (a b) - "Sort overlay A and B." - (< (overlay-start a) (overlay-start b))) - -(defsubst doom-modeline--iedit () - "Show the number of iedit regions matches + what match you're on." - (when (and (bound-and-true-p iedit-mode) - (bound-and-true-p iedit-occurrences-overlays)) - (propertize - (let ((this-oc (or (let ((inhibit-message t)) - (iedit-find-current-occurrence-overlay)) - (save-excursion (iedit-prev-occurrence) - (iedit-find-current-occurrence-overlay)))) - (length (length iedit-occurrences-overlays))) - (format " %s/%d " - (if this-oc - (- length - (length (memq this-oc (sort (append iedit-occurrences-overlays nil) - #'doom-modeline-themes--overlay-sort))) - -1) - "-") - length)) - 'face (doom-modeline-face 'doom-modeline-panel)))) - -(defsubst doom-modeline--symbol-overlay () - "Show the number of matches for symbol overlay." - (when (and (doom-modeline--active) - (bound-and-true-p symbol-overlay-keywords-alist) - (not (bound-and-true-p symbol-overlay-temp-symbol)) - (not (bound-and-true-p iedit-mode))) - (let* ((keyword (symbol-overlay-assoc (symbol-overlay-get-symbol t))) - (symbol (car keyword)) - (before (symbol-overlay-get-list -1 symbol)) - (after (symbol-overlay-get-list 1 symbol)) - (count (length before))) - (if (symbol-overlay-assoc symbol) - (propertize - (format (concat " %d/%d " (and (cadr keyword) "in scope ")) - (+ count 1) - (+ count (length after))) - 'face (doom-modeline-face 'doom-modeline-panel)))))) - -(defsubst doom-modeline--multiple-cursors () - "Show the number of multiple cursors." - (cl-destructuring-bind (count . face) - (cond ((bound-and-true-p multiple-cursors-mode) - (cons (mc/num-cursors) - (doom-modeline-face 'doom-modeline-panel))) - ((bound-and-true-p evil-mc-cursor-list) - (cons (length evil-mc-cursor-list) - (doom-modeline-face (if evil-mc-frozen - 'doom-modeline-bar - 'doom-modeline-panel)))) - ((cons nil nil))) - (when count - (concat (propertize " " 'face face) - (if (doom-modeline-icon-displayable-p) - (doom-modeline-icon 'faicon "nf-fa-i_cursor" "" "" :face face) - (propertize "I" - 'face `(:inherit ,face :height 1.4 :weight normal) - 'display '(raise -0.1))) - (propertize " " - 'face `(:inherit (variable-pitch ,face))) - (propertize (format "%d " count) - 'face face))))) - -(defsubst doom-modeline--phi-search () - "Show the number of matches for `phi-search' and `phi-replace'." - (when (and (doom-modeline--active) - (bound-and-true-p phi-search--overlays)) - (let ((total (length phi-search--overlays)) - (selection phi-search--selection)) - (when selection - (propertize - (format " %d/%d " (1+ selection) total) - 'face (doom-modeline-face 'doom-modeline-panel)))))) - -(defun doom-modeline--override-phi-search (orig-fun &rest args) - "Override the mode-line of `phi-search' and `phi-replace'. -Apply ORIG-FUN with ARGS." - (if (bound-and-true-p doom-modeline-mode) - (apply orig-fun mode-line-format (cdr args)) - (apply orig-fun args))) -(advice-add #'phi-search--initialize :around #'doom-modeline--override-phi-search) - -(defsubst doom-modeline--buffer-size () - "Show buffer size." - (when size-indication-mode - (let ((sep (doom-modeline-spc))) - (concat sep - (propertize "%I" - 'face (doom-modeline-face) - 'help-echo "Buffer size -mouse-1: Display Line and Column Mode Menu" - 'mouse-face 'doom-modeline-highlight - 'local-map mode-line-column-line-number-mode-map) - sep)))) - -(doom-modeline-def-segment matches - "Displays matches. - -Including: -1. the currently recording macro, 2. A current/total for the -current search term (with `anzu'), 3. The number of substitutions being -conducted with `evil-ex-substitute', and/or 4. The number of active `iedit' -regions, 5. The current/total for the highlight term (with `symbol-overlay'), -6. The number of active `multiple-cursors'." - (let ((meta (concat (doom-modeline--macro-recording) - (doom-modeline--anzu) - (doom-modeline--phi-search) - (doom-modeline--evil-substitute) - (doom-modeline--iedit) - (doom-modeline--symbol-overlay) - (doom-modeline--multiple-cursors)))) - (or (and (not (string-empty-p meta)) meta) - (doom-modeline--buffer-size)))) - -(doom-modeline-def-segment buffer-size - "Display buffer size." - (doom-modeline--buffer-size)) - -;; -;; Media -;; - -(doom-modeline-def-segment media-info - "Metadata regarding the current file, such as dimensions for images." - ;; TODO: Include other information - (cond ((eq major-mode 'image-mode) - (cl-destructuring-bind (width . height) - (when (fboundp 'image-size) - (image-size (image-get-display-property) :pixels)) - (format " %dx%d " width height))))) - - -;; -;; Bars -;; - -(defvar doom-modeline--bar-active nil) -(defvar doom-modeline--bar-inactive nil) - -(defsubst doom-modeline--bar () - "The default bar regulates the height of the mode-line in GUI." - (unless (and doom-modeline--bar-active doom-modeline--bar-inactive) - (let ((width doom-modeline-bar-width) - (height (max doom-modeline-height (doom-modeline--font-height)))) - (setq doom-modeline--bar-active - (doom-modeline--create-bar-image 'doom-modeline-bar width height) - doom-modeline--bar-inactive - (doom-modeline--create-bar-image - 'doom-modeline-bar-inactive width height)))) - (if (doom-modeline--active) - doom-modeline--bar-active - doom-modeline--bar-inactive)) - -(defun doom-modeline-refresh-bars () - "Refresh mode-line bars on next redraw." - (setq doom-modeline--bar-active nil - doom-modeline--bar-inactive nil)) - -(cl-defstruct doom-modeline--hud-cache active inactive top-margin bottom-margin) - -(defsubst doom-modeline--hud () - "Powerline's hud segment reimplemented in the style of Doom's bar segment." - (let* ((ws (window-start)) - (we (window-end)) - (bs (buffer-size)) - (height (max doom-modeline-height (doom-modeline--font-height))) - (top-margin (if (zerop bs) - 0 - (/ (* height (1- ws)) bs))) - (bottom-margin (if (zerop bs) - 0 - (max 0 (/ (* height (- bs we 1)) bs)))) - (cache (or (window-parameter nil 'doom-modeline--hud-cache) - (set-window-parameter - nil - 'doom-modeline--hud-cache - (make-doom-modeline--hud-cache))))) - (unless (and (doom-modeline--hud-cache-active cache) - (doom-modeline--hud-cache-inactive cache) - (= top-margin (doom-modeline--hud-cache-top-margin cache)) - (= bottom-margin - (doom-modeline--hud-cache-bottom-margin cache))) - (setf (doom-modeline--hud-cache-active cache) - (doom-modeline--create-hud-image - 'doom-modeline-bar 'default doom-modeline-bar-width - height top-margin bottom-margin) - (doom-modeline--hud-cache-inactive cache) - (doom-modeline--create-hud-image - 'doom-modeline-bar-inactive 'default doom-modeline-bar-width - height top-margin bottom-margin) - (doom-modeline--hud-cache-top-margin cache) top-margin - (doom-modeline--hud-cache-bottom-margin cache) bottom-margin)) - (if (doom-modeline--active) - (doom-modeline--hud-cache-active cache) - (doom-modeline--hud-cache-inactive cache)))) - -(defun doom-modeline-invalidate-huds () - "Invalidate all cached hud images." - (dolist (frame (frame-list)) - (dolist (window (window-list frame)) - (set-window-parameter window 'doom-modeline--hud-cache nil)))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-height - (lambda (_sym val op _where) - (when (and (eq op 'set) (integerp val)) - (doom-modeline-refresh-bars) - (doom-modeline-invalidate-huds)))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-bar-width - (lambda (_sym val op _where) - (when (and (eq op 'set) (integerp val)) - (doom-modeline-refresh-bars) - (doom-modeline-invalidate-huds)))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-icon - (lambda (_sym _val op _where) - (when (eq op 'set) - (doom-modeline-refresh-bars) - (doom-modeline-invalidate-huds)))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-unicode-fallback - (lambda (_sym _val op _where) - (when (eq op 'set) - (doom-modeline-refresh-bars) - (doom-modeline-invalidate-huds)))) - -(add-hook 'window-configuration-change-hook #'doom-modeline-refresh-bars) -(add-hook 'window-configuration-change-hook #'doom-modeline-invalidate-huds) - -(doom-modeline-def-segment bar - "The bar regulates the height of the `doom-modeline' in GUI." - (when (display-graphic-p) - (concat - (if doom-modeline-hud - (doom-modeline--hud) - (doom-modeline--bar)) - (doom-modeline-spc)))) - -(doom-modeline-def-segment hud - "Powerline's hud segment reimplemented in the style of bar segment." - (when (display-graphic-p) - (concat - (doom-modeline--hud) - (doom-modeline-spc)))) - - -;; -;; Window number -;; - -;; HACK: `ace-window-display-mode' should respect the ignore buffers. -(defun doom-modeline-aw-update () - "Update ace-window-path window parameter for all windows. -Ensure all windows are labeled so the user can select a specific -one. The ignored buffers are excluded unless `aw-ignore-on' is nil." - (let ((ignore-window-parameters t)) - (avy-traverse - (avy-tree (aw-window-list) aw-keys) - (lambda (path leaf) - (set-window-parameter - leaf 'ace-window-path - (propertize - (apply #'string (reverse path)) - 'face 'aw-mode-line-face)))))) -(advice-add #'aw-update :override #'doom-modeline-aw-update) - -;; Remove original window number of `ace-window-display-mode'. -(add-hook 'ace-window-display-mode-hook - (lambda () - (setq-default mode-line-format - (assq-delete-all 'ace-window-display-mode - (default-value 'mode-line-format))))) - -(advice-add #'window-numbering-install-mode-line :override #'ignore) -(advice-add #'window-numbering-clear-mode-line :override #'ignore) -(advice-add #'winum--install-mode-line :override #'ignore) -(advice-add #'winum--clear-mode-line :override #'ignore) - -(doom-modeline-def-segment window-number - "The current window number." - (let ((num (cond - ((bound-and-true-p ace-window-display-mode) - (aw-update) - (window-parameter (selected-window) 'ace-window-path)) - ((bound-and-true-p winum-mode) - (setq winum-auto-setup-mode-line nil) - (winum-get-number-string)) - ((bound-and-true-p window-numbering-mode) - (window-numbering-get-number-string)) - (t "")))) - (when (and (length> num 0) - (length> (cl-mapcan - (lambda (frame) - ;; Exclude minibuffer, tooltip and child frames - (unless (or (and (fboundp 'frame-parent) (frame-parent frame)) - (string= (frame-parameter frame 'name) - (alist-get 'name (bound-and-true-p tooltip-frame-parameters)))) - (window-list frame 'never))) - (visible-frame-list)) - 1)) - (propertize (format " %s " num) - 'face (doom-modeline-face 'doom-modeline-buffer-major-mode))))) - - -;; -;; Workspace -;; - -(doom-modeline-def-segment workspace-name - "The current workspace name or number. -Requires `eyebrowse-mode' to be enabled or `tab-bar-mode' tabs to be created." - (when doom-modeline-workspace-name - (when-let - ((name (cond - ((and (bound-and-true-p eyebrowse-mode) - (length> (eyebrowse--get 'window-configs) 1)) - (setq mode-line-misc-info - (assq-delete-all 'eyebrowse-mode mode-line-misc-info)) - (when-let* - ((num (eyebrowse--get 'current-slot)) - (tag (nth 2 (assoc num (eyebrowse--get 'window-configs))))) - (if (length> tag 0) tag (int-to-string num)))) - ((and (fboundp 'tab-bar-mode) - (length> (frame-parameter nil 'tabs) 1)) - (let* ((current-tab (tab-bar--current-tab)) - (tab-index (tab-bar--current-tab-index)) - (explicit-name (alist-get 'explicit-name current-tab)) - (tab-name (alist-get 'name current-tab))) - (if explicit-name tab-name (+ 1 tab-index))))))) - (propertize (format " %s " name) - 'face (doom-modeline-face 'doom-modeline-buffer-major-mode))))) - - -;; -;; Perspective -;; - -(defvar-local doom-modeline--persp-name nil) -(defun doom-modeline-update-persp-name (&rest _) - "Update perspective name in mode-line." - (setq doom-modeline--persp-name - ;; Support `persp-mode', while not support `perspective' - (when (and doom-modeline-persp-name - (bound-and-true-p persp-mode) - (fboundp 'safe-persp-name) - (fboundp 'get-current-persp)) - (let* ((persp (get-current-persp)) - (name (safe-persp-name persp)) - (face (if (and persp - (not (persp-contain-buffer-p (current-buffer) persp))) - 'doom-modeline-persp-buffer-not-in-persp - 'doom-modeline-persp-name)) - (icon (doom-modeline-icon 'octicon "nf-oct-repo" "🖿" "#" - :face `(:inherit ,face :slant normal)))) - (when (or doom-modeline-display-default-persp-name - (not (string-equal persp-nil-name name))) - (concat " " - (propertize (concat (and doom-modeline-persp-icon - (concat icon - (propertize - " " - 'display '((space :relative-width 0.5))))) - (propertize name 'face face)) - 'help-echo "mouse-1: Switch perspective -mouse-2: Show help for minor mode" - 'mouse-face 'doom-modeline-highlight - 'local-map (let ((map (make-sparse-keymap))) - (define-key map [mode-line mouse-1] - #'persp-switch) - (define-key map [mode-line mouse-2] - (lambda () - (interactive) - (describe-function 'persp-mode))) - map)) - " ")))))) - -(add-hook 'buffer-list-update-hook #'doom-modeline-update-persp-name) -(add-hook 'find-file-hook #'doom-modeline-update-persp-name) -(add-hook 'persp-activated-functions #'doom-modeline-update-persp-name) -(add-hook 'persp-renamed-functions #'doom-modeline-update-persp-name) -(advice-add #'lv-message :after #'doom-modeline-update-persp-name) - -(doom-modeline-def-segment persp-name - "The current perspective name." - (when (doom-modeline--segment-visible 'persp-name) - doom-modeline--persp-name)) - - -;; -;; Misc info -;; - -(doom-modeline-def-segment misc-info - "Mode line construct for miscellaneous information. -By default, this shows the information specified by `global-mode-string'." - (when (and (doom-modeline--segment-visible 'misc-info) - (or doom-modeline-display-misc-in-all-mode-lines - (doom-modeline--active))) - (doom-modeline-display-text - (format-mode-line mode-line-misc-info)))) - - -;; -;; Position -;; - -(doom-modeline-def-segment buffer-position - "The buffer position information." - (let ((visible (doom-modeline--segment-visible 'buffer-position)) - (sep (doom-modeline-spc)) - (wsep (doom-modeline-wspc)) - (face (doom-modeline-face)) - (help-echo "Buffer percentage\n\ -mouse-1: Display Line and Column Mode Menu") - (mouse-face 'doom-modeline-highlight) - (local-map mode-line-column-line-number-mode-map)) - `(,wsep - - ;; Line and column - (:propertize - ((line-number-mode - (column-number-mode - (doom-modeline-column-zero-based - doom-modeline-position-column-line-format - ,(string-replace - "%c" "%C" (car doom-modeline-position-column-line-format))) - doom-modeline-position-line-format) - (column-number-mode - (doom-modeline-column-zero-based - doom-modeline-position-column-format - ,(string-replace - "%c" "%C" (car doom-modeline-position-column-format))))) - (doom-modeline-total-line-number - ,(format "/%d" (line-number-at-pos (point-max))))) - face ,face - help-echo ,help-echo - mouse-face ,mouse-face - local-map ,local-map) - - ((or line-number-mode column-number-mode) - ,sep) - - ;; Position - (,visible - ,(cond - ((and (bound-and-true-p nyan-mode) - (>= (window-width) nyan-minimum-window-width)) - (concat sep (nyan-create) sep)) - ((and (bound-and-true-p poke-line-mode) - (>= (window-width) poke-line-minimum-window-width)) - (concat sep (poke-line-create) sep)) - ((and (bound-and-true-p mlscroll-mode) - (>= (window-width) mlscroll-minimum-current-width)) - (concat - sep - (let ((mlscroll-right-align nil)) - (format-mode-line (mlscroll-mode-line))) - sep)) - ((and (bound-and-true-p sml-modeline-mode) - (>= (window-width) sml-modeline-len)) - (concat sep (sml-modeline-create) sep)) - (t ""))) - - ;; Percent position - (doom-modeline-percent-position - ((:propertize ("" doom-modeline-percent-position) - face ,face - help-echo ,help-echo - mouse-face ,mouse-face - local-map ,local-map) - ,sep))))) - -;; -;; Party parrot -;; -(doom-modeline-def-segment parrot - "The party parrot animated icon. Requires `parrot-mode' to be enabled." - (when (and (doom-modeline--segment-visible 'parrot) - (bound-and-true-p parrot-mode)) - (concat (doom-modeline-wspc) - (parrot-create) - (doom-modeline-spc)))) - -;; -;; Modals (evil, overwrite, god, ryo and xah-fly-keys, etc.) -;; - -(defun doom-modeline--modal-icon (text face help-echo &optional icon unicode) - "Display the model icon with FACE and HELP-ECHO. -TEXT is alternative if icon is not available." - (propertize (doom-modeline-icon - 'mdicon - (and doom-modeline-modal-icon - (or (and doom-modeline-modal-modern-icon icon) - "nf-md-record")) - (or (and doom-modeline-modal-modern-icon unicode) "●") - text - :face (doom-modeline-face face)) - 'help-echo help-echo)) - -(defsubst doom-modeline--evil () - "The current evil state. Requires `evil-mode' to be enabled." - (when (bound-and-true-p evil-local-mode) - (doom-modeline--modal-icon - (let ((tag (evil-state-property evil-state :tag t))) - (if (stringp tag) tag (funcall tag))) - (cond - ((evil-normal-state-p) 'doom-modeline-evil-normal-state) - ((evil-emacs-state-p) 'doom-modeline-evil-emacs-state) - ((evil-insert-state-p) 'doom-modeline-evil-insert-state) - ((evil-motion-state-p) 'doom-modeline-evil-motion-state) - ((evil-visual-state-p) 'doom-modeline-evil-visual-state) - ((evil-operator-state-p) 'doom-modeline-evil-operator-state) - ((evil-replace-state-p) 'doom-modeline-evil-replace-state) - (t 'doom-modeline-evil-normal-state)) - (evil-state-property evil-state :name t) - (cond - ((evil-normal-state-p) "nf-md-alpha_n_circle") - ((evil-emacs-state-p) "nf-md-alpha_e_circle") - ((evil-insert-state-p) "nf-md-alpha_i_circle") - ((evil-motion-state-p) "nf-md-alpha_m_circle") - ((evil-visual-state-p) "nf-md-alpha_v_circle") - ((evil-operator-state-p) "nf-md-alpha_o_circle") - ((evil-replace-state-p) "nf-md-alpha_r_circle") - (t "nf-md-alpha_n_circle")) - (cond - ((evil-normal-state-p) "🅝") - ((evil-emacs-state-p) "🅔") - ((evil-insert-state-p) "🅘") - ((evil-motion-state-p) "🅜") - ((evil-visual-state-p) "🅥") - ((evil-operator-state-p) "🅞") - ((evil-replace-state-p) "🅡") - (t "🅝"))))) - -(defsubst doom-modeline--overwrite () - "The current overwrite state which is enabled by command `overwrite-mode'." - (when (and (bound-and-true-p overwrite-mode) - (not (bound-and-true-p evil-local-mode))) - (doom-modeline--modal-icon - "<W>" 'doom-modeline-overwrite "Overwrite mode" - "nf-md-marker" "🅦"))) - -(defsubst doom-modeline--god () - "The current god state which is enabled by the command `god-mode'." - (when (bound-and-true-p god-local-mode) - (doom-modeline--modal-icon - "<G>" 'doom-modeline-god "God mode" - "nf-md-account_circle" "🅖"))) - -(defsubst doom-modeline--ryo () - "The current ryo-modal state which is enabled by the command `ryo-modal-mode'." - (when (bound-and-true-p ryo-modal-mode) - (doom-modeline--modal-icon - "<R>" 'doom-modeline-ryo "Ryo modal" - "nf-md-star_circle" "✪"))) - -(defsubst doom-modeline--xah-fly-keys () - "The current `xah-fly-keys' state." - (when (bound-and-true-p xah-fly-keys) - (if xah-fly-insert-state-p - (doom-modeline--modal-icon - "<I>" 'doom-modeline-fly-insert-state "Xah-fly insert mode" - "nf-md-airplane_edit" "🛧") - (doom-modeline--modal-icon - "<C>" 'doom-modeline-fly-normal-state "Xah-fly command mode" - "nf-md-airplane_cog" "🛧")))) - -(defsubst doom-modeline--boon () - "The current Boon state. Requires `boon-mode' to be enabled." - (when (bound-and-true-p boon-local-mode) - (doom-modeline--modal-icon - (boon-state-string) - (cond - (boon-command-state 'doom-modeline-boon-command-state) - (boon-insert-state 'doom-modeline-boon-insert-state) - (boon-special-state 'doom-modeline-boon-special-state) - (boon-off-state 'doom-modeline-boon-off-state) - (t 'doom-modeline-boon-off-state)) - (boon-modeline-string) - "nf-md-coffee" "🍵"))) - -(defsubst doom-modeline--meow () - "The current Meow state. Requires `meow-mode' to be enabled." - (when (bound-and-true-p meow-mode) - (if (doom-modeline--active) - meow--indicator - (propertize (substring-no-properties meow--indicator) - 'face - 'mode-line-inactive)))) - -(doom-modeline-def-segment modals - "Displays modal editing states. - -Including `evil', `overwrite', `god', `ryo' and `xha-fly-kyes', etc." - (when doom-modeline-modal - (let* ((evil (doom-modeline--evil)) - (ow (doom-modeline--overwrite)) - (god (doom-modeline--god)) - (ryo (doom-modeline--ryo)) - (xf (doom-modeline--xah-fly-keys)) - (boon (doom-modeline--boon)) - (vsep (doom-modeline-vspc)) - (meow (doom-modeline--meow)) - (sep (and (or evil ow god ryo xf boon) (doom-modeline-spc)))) - (concat sep - (and evil (concat evil (and (or ow god ryo xf boon meow) vsep))) - (and ow (concat ow (and (or god ryo xf boon meow) vsep))) - (and god (concat god (and (or ryo xf boon meow) vsep))) - (and ryo (concat ryo (and (or xf boon meow) vsep))) - (and xf (concat xf (and (or boon meow) vsep))) - (and boon (concat boon (and meow vsep))) - meow - sep)))) - -;; -;; Objed state -;; - -(defvar doom-modeline--objed-active nil) - -(defun doom-modeline-update-objed (_ &optional reset) - "Update `objed' status, inactive when RESET is true." - (setq doom-modeline--objed-active (not reset))) - -(setq objed-modeline-setup-func #'doom-modeline-update-objed) - -(doom-modeline-def-segment objed-state () - "The current objed state." - (when (and doom-modeline--objed-active - (doom-modeline--active)) - (propertize (format " %s(%s) " - (symbol-name objed--object) - (char-to-string (aref (symbol-name objed--obj-state) 0))) - 'face 'doom-modeline-evil-emacs-state - 'help-echo (format "Objed object: %s (%s)" - (symbol-name objed--object) - (symbol-name objed--obj-state))))) - - -;; -;; Input method -;; - -(doom-modeline-def-segment input-method - "The current input method." - (when-let ((im (cond - (current-input-method - current-input-method-title) - ((and (bound-and-true-p evil-local-mode) - (bound-and-true-p evil-input-method)) - (nth 3 (assoc default-input-method input-method-alist))) - (t nil))) - (sep (doom-modeline-spc))) - (concat - sep - (propertize im - 'face (doom-modeline-face - (if (and (bound-and-true-p rime-mode) - (equal current-input-method "rime")) - (if (and (rime--should-enable-p) - (not (rime--should-inline-ascii-p))) - 'doom-modeline-input-method - 'doom-modeline-input-method-alt) - 'doom-modeline-input-method)) - 'help-echo (concat - "Current input method: " - current-input-method - "\n\ -mouse-2: Disable input method\n\ -mouse-3: Describe current input method") - 'mouse-face 'doom-modeline-highlight - 'local-map mode-line-input-method-map) - sep))) - - -;; -;; Info -;; - -(doom-modeline-def-segment info-nodes - "The topic and nodes in the Info buffer." - (concat - " (" - ;; topic - (propertize (if (stringp Info-current-file) - (replace-regexp-in-string - "%" "%%" - (file-name-sans-extension - (file-name-nondirectory Info-current-file))) - (format "*%S*" Info-current-file)) - 'face (doom-modeline-face 'doom-modeline-info)) - ") " - ;; node - (when Info-current-node - (propertize (replace-regexp-in-string - "%" "%%" Info-current-node) - 'face (doom-modeline-face 'doom-modeline-buffer-path) - 'help-echo - "mouse-1: scroll forward, mouse-3: scroll back" - 'mouse-face 'doom-modeline-highlight - 'local-map Info-mode-line-node-keymap)))) - - -;; -;; REPL -;; - -(defun doom-modeline-repl-icon (text face) - "Display REPL icon (or TEXT in terminal) with FACE." - (doom-modeline-icon 'faicon "nf-fa-terminal" "$" text :face face)) - -(defvar doom-modeline--cider nil) - -(defun doom-modeline-update-cider () - "Update cider repl state." - (setq doom-modeline--cider - (let* ((connected (cider-connected-p)) - (face (if connected 'doom-modeline-repl-success 'doom-modeline-repl-warning)) - (repl-buffer (cider-current-repl nil nil)) - (cider-info (when repl-buffer - (cider--connection-info repl-buffer t))) - (icon (doom-modeline-repl-icon "REPL" face))) - (propertize icon - 'help-echo - (if connected - (format "CIDER Connected %s\nmouse-2: CIDER quit" cider-info) - "CIDER Disconnected\nmouse-1: CIDER jack-in") - 'mouse-face 'doom-modeline-highlight - 'local-map (let ((map (make-sparse-keymap))) - (if connected - (define-key map [mode-line mouse-2] - #'cider-quit) - (define-key map [mode-line mouse-1] - #'cider-jack-in)) - map))))) - -(add-hook 'cider-connected-hook #'doom-modeline-update-cider) -(add-hook 'cider-disconnected-hook #'doom-modeline-update-cider) -(add-hook 'cider-mode-hook #'doom-modeline-update-cider) - -(doom-modeline-def-segment repl - "The REPL state." - (when doom-modeline-repl - (when-let ((icon (when (bound-and-true-p cider-mode) - doom-modeline--cider)) - (sep (doom-modeline-spc))) - (concat - sep - (doom-modeline-display-icon icon) - sep)))) - - -;; -;; LSP -;; - -(defun doom-modeline-lsp-icon (text face) - "Display LSP icon (or TEXT in terminal) with FACE." - (if doom-modeline-lsp-icon - (doom-modeline-icon 'octicon "nf-oct-rocket" "🚀" text :face face) - (propertize text 'face face))) - -(defvar-local doom-modeline--lsp nil) -(defun doom-modeline-update-lsp (&rest _) - "Update `lsp-mode' state." - (setq doom-modeline--lsp - (let* ((workspaces (lsp-workspaces)) - (face (if workspaces 'doom-modeline-lsp-success 'doom-modeline-lsp-warning)) - (icon (doom-modeline-lsp-icon "LSP" face))) - (propertize icon - 'help-echo - (if workspaces - (concat "LSP Connected " - (string-join - (mapcar (lambda (w) - (format "[%s]\n" (lsp--workspace-print w))) - workspaces)) - "C-mouse-1: Switch to another workspace folder -mouse-1: Describe current session -mouse-2: Quit server -mouse-3: Reconnect to server") - "LSP Disconnected -mouse-1: Reload to start server") - 'mouse-face 'doom-modeline-highlight - 'local-map (let ((map (make-sparse-keymap))) - (if workspaces - (progn - (define-key map [mode-line C-mouse-1] - #'lsp-workspace-folders-open) - (define-key map [mode-line mouse-1] - #'lsp-describe-session) - (define-key map [mode-line mouse-2] - #'lsp-workspace-shutdown) - (define-key map [mode-line mouse-3] - #'lsp-workspace-restart)) - (progn - (define-key map [mode-line mouse-1] - (lambda () - (interactive) - (ignore-errors (revert-buffer t t)))))) - map))))) -(add-hook 'lsp-before-initialize-hook #'doom-modeline-update-lsp) -(add-hook 'lsp-after-initialize-hook #'doom-modeline-update-lsp) -(add-hook 'lsp-after-uninitialized-functions #'doom-modeline-update-lsp) -(add-hook 'lsp-before-open-hook #'doom-modeline-update-lsp) -(add-hook 'lsp-after-open-hook #'doom-modeline-update-lsp) - -(defun doom-modeline--eglot-pending-count (server) - "Get count of pending eglot requests to SERVER." - (if (fboundp 'jsonrpc-continuation-count) - (jsonrpc-continuation-count server) - (hash-table-count (jsonrpc--request-continuations server)))) - -(defvar-local doom-modeline--eglot nil) -(defun doom-modeline-update-eglot () - "Update `eglot' state." - (setq doom-modeline--eglot - (pcase-let* ((server (and (eglot-managed-p) (eglot-current-server))) - (nick (and server (eglot--project-nickname server))) - (pending (and server (doom-modeline--eglot-pending-count server))) - (last-error (and server (jsonrpc-last-error server))) - (face (cond (last-error 'doom-modeline-lsp-error) - ((and pending (cl-plusp pending)) 'doom-modeline-lsp-warning) - (nick 'doom-modeline-lsp-success) - (t 'doom-modeline-lsp-warning))) - (icon (doom-modeline-lsp-icon "EGLOT" face))) - (propertize icon - 'help-echo (cond - (last-error - (format "EGLOT\nAn error occured: %s -mouse-3: Clear this status" (plist-get last-error :message))) - ((and pending (cl-plusp pending)) - (format "EGLOT\n%d outstanding requests" pending)) - (nick (format "EGLOT Connected (%s/%s) -C-mouse-1: Go to server errors -mouse-1: Go to server events -mouse-2: Quit server -mouse-3: Reconnect to server" nick (eglot--major-modes server))) - (t "EGLOT Disconnected -mouse-1: Start server")) - 'mouse-face 'doom-modeline-highlight - 'local-map (let ((map (make-sparse-keymap))) - (cond (last-error - (define-key map [mode-line mouse-3] - #'eglot-clear-status)) - ((and pending (cl-plusp pending)) - (define-key map [mode-line mouse-3] - #'eglot-forget-pending-continuations)) - (nick - (define-key map [mode-line C-mouse-1] - #'eglot-stderr-buffer) - (define-key map [mode-line mouse-1] - #'eglot-events-buffer) - (define-key map [mode-line mouse-2] - #'eglot-shutdown) - (define-key map [mode-line mouse-3] - #'eglot-reconnect)) - (t (define-key map [mode-line mouse-1] - #'eglot))) - map))))) -(add-hook 'eglot-managed-mode-hook #'doom-modeline-update-eglot) - -(defvar-local doom-modeline--tags nil) -(defun doom-modeline-update-tags () - "Update tags state." - (setq doom-modeline--tags - (propertize - (doom-modeline-lsp-icon "TAGS" 'doom-modeline-lsp-success) - 'help-echo "TAGS: Citre mode -mouse-1: Toggle citre mode" - 'mouse-face 'doom-modeline-highlight - 'local-map (make-mode-line-mouse-map 'mouse-1 #'citre-mode)))) -(add-hook 'citre-mode-hook #'doom-modeline-update-tags) - -(defun doom-modeline-update-lsp-icon () - "Update lsp icon." - (cond ((bound-and-true-p lsp-mode) - (doom-modeline-update-lsp)) - ((bound-and-true-p eglot--managed-mode) - (doom-modeline-update-eglot)) - ((bound-and-true-p citre-mode) - (doom-modeline-update-tags)))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-lsp-icon - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-lsp-icon val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (doom-modeline-update-lsp-icon)))))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-icon - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-icon val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (doom-modeline-update-lsp-icon)))))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-unicode-fallback - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-unicode-fallback val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (doom-modeline-update-lsp-icon)))))) - -(doom-modeline-def-segment lsp - "The LSP server state." - (when doom-modeline-lsp - (when-let ((icon (cond ((bound-and-true-p lsp-mode) - doom-modeline--lsp) - ((bound-and-true-p eglot--managed-mode) - doom-modeline--eglot) - ((bound-and-true-p citre-mode) - doom-modeline--tags))) - (sep (doom-modeline-spc))) - (concat - sep - (doom-modeline-display-icon icon) - sep)))) - -(defun doom-modeline-override-eglot () - "Override `eglot' mode-line." - (if (and doom-modeline-lsp - (bound-and-true-p doom-modeline-mode)) - (setq mode-line-misc-info - (delq (assq 'eglot--managed-mode mode-line-misc-info) mode-line-misc-info)) - (add-to-list 'mode-line-misc-info - `(eglot--managed-mode (" [" eglot--mode-line-format "] "))))) -(add-hook 'eglot-managed-mode-hook #'doom-modeline-override-eglot) -(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-eglot) - -(doom-modeline-add-variable-watcher - 'doom-modeline-battery - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-lsp val) - (doom-modeline-override-eglot)))) - - -;; -;; GitHub -;; - -(defvar doom-modeline--github-notification-number 0) -(defvar doom-modeline-before-github-fetch-notification-hook nil - "Hooks before fetching GitHub notifications. -Example: - (add-hook \\='doom-modeline-before-github-fetch-notification-hook - #\\='auth-source-pass-enable)") - -(defvar doom-modeline-after-github-fetch-notification-hook nil - "Hooks after fetching GitHub notifications.") - -(defun doom-modeline--github-fetch-notifications () - "Fetch GitHub notifications." - (when (and doom-modeline-github - (require 'async nil t)) - (async-start - `(lambda () - ,(async-inject-variables - "\\`\\(load-path\\|auth-sources\\|doom-modeline-before-github-fetch-notification-hook\\)\\'") - (run-hooks 'doom-modeline-before-github-fetch-notification-hook) - (when (require 'ghub nil t) - (with-timeout (10) - (ignore-errors - (when-let* ((username (ghub--username ghub-default-host)) - (token (or (ghub--token ghub-default-host username 'forge t) - (ghub--token ghub-default-host username 'ghub t)))) - (ghub-get "/notifications" - '((notifications . t)) - :host ghub-default-host - :username username - :auth token - :unpaginate t - :noerror t)))))) - (lambda (result) - (message "") ; suppress message - (setq doom-modeline--github-notification-number (length result)) - (run-hooks 'doom-modeline-after-github-fetch-notification-hook))))) - -(defvar doom-modeline--github-timer nil) -(defun doom-modeline-github-timer () - "Start/Stop the timer for GitHub fetching." - (if (timerp doom-modeline--github-timer) - (cancel-timer doom-modeline--github-timer)) - (setq doom-modeline--github-timer - (and doom-modeline-github - (run-with-idle-timer 30 - doom-modeline-github-interval - #'doom-modeline--github-fetch-notifications)))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-github - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-github val) - (doom-modeline-github-timer)))) - -(doom-modeline-github-timer) - -(doom-modeline-def-segment github - "The GitHub notifications." - (when (and doom-modeline-github - (doom-modeline--segment-visible 'github) - (numberp doom-modeline--github-notification-number)) - (let ((sep (doom-modeline-spc))) - (concat - sep - (propertize - (concat - (doom-modeline-icon 'octicon "nf-oct-mark_github" "🔔" "&" - :face 'doom-modeline-notification) - (and (> doom-modeline--github-notification-number 0) (doom-modeline-vspc)) - (propertize - (cond - ((<= doom-modeline--github-notification-number 0) "") - ((> doom-modeline--github-notification-number 99) "99+") - (t (number-to-string doom-modeline--github-notification-number))) - 'face '(:inherit - (doom-modeline-unread-number doom-modeline-notification)))) - 'help-echo "Github Notifications -mouse-1: Show notifications -mouse-3: Fetch notifications" - 'mouse-face 'doom-modeline-highlight - 'local-map (let ((map (make-sparse-keymap))) - (define-key map [mode-line mouse-1] - (lambda () - "Open GitHub notifications page." - (interactive) - (run-with-idle-timer 300 nil #'doom-modeline--github-fetch-notifications) - (browse-url "https://github.com/notifications"))) - (define-key map [mode-line mouse-3] - (lambda () - "Fetching GitHub notifications." - (interactive) - (message "Fetching GitHub notifications...") - (doom-modeline--github-fetch-notifications))) - map)) - sep)))) - - -;; -;; Debug states -;; - -;; Highlight the doom-modeline while debugging. -(defvar-local doom-modeline--debug-cookie nil) -(defun doom-modeline--debug-visual (&rest _) - "Update the face of mode-line for debugging." - (mapc (lambda (buffer) - (with-current-buffer buffer - (setq doom-modeline--debug-cookie - (face-remap-add-relative 'doom-modeline 'doom-modeline-debug-visual)) - (force-mode-line-update))) - (buffer-list))) - -(defun doom-modeline--normal-visual (&rest _) - "Restore the face of mode-line." - (mapc (lambda (buffer) - (with-current-buffer buffer - (when doom-modeline--debug-cookie - (face-remap-remove-relative doom-modeline--debug-cookie) - (force-mode-line-update)))) - (buffer-list))) - -(add-hook 'dap-session-created-hook #'doom-modeline--debug-visual) -(add-hook 'dap-terminated-hook #'doom-modeline--normal-visual) - -(defun doom-modeline-debug-icon (face) - "Display debug icon with FACE and ARGS." - (doom-modeline-icon 'codicon "nf-cod-debug" "🐛" "!" :face face)) - -(defun doom-modeline--debug-dap () - "The current `dap-mode' state." - (when (and (bound-and-true-p dap-mode) - (bound-and-true-p lsp-mode)) - (when-let ((session (dap--cur-session))) - (when (dap--session-running session) - (propertize (doom-modeline-debug-icon 'doom-modeline-info) - 'help-echo (format "DAP (%s - %s) -mouse-1: Display debug hydra -mouse-2: Display recent configurations -mouse-3: Disconnect session" - (dap--debug-session-name session) - (dap--debug-session-state session)) - 'mouse-face 'doom-modeline-highlight - 'local-map (let ((map (make-sparse-keymap))) - (define-key map [mode-line mouse-1] - #'dap-hydra) - (define-key map [mode-line mouse-2] - #'dap-debug-recent) - (define-key map [mode-line mouse-3] - #'dap-disconnect) - map)))))) - -(defvar-local doom-modeline--debug-dap nil) -(defun doom-modeline-update-debug-dap (&rest _) - "Update dap debug state." - (setq doom-modeline--debug-dap (doom-modeline--debug-dap))) - -(add-hook 'dap-session-created-hook #'doom-modeline-update-debug-dap) -(add-hook 'dap-session-changed-hook #'doom-modeline-update-debug-dap) -(add-hook 'dap-terminated-hook #'doom-modeline-update-debug-dap) - -(defsubst doom-modeline--debug-edebug () - "The current `edebug' state." - (when (bound-and-true-p edebug-mode) - (propertize (doom-modeline-debug-icon 'doom-modeline-info) - 'help-echo (format "EDebug (%s) -mouse-1: Show help -mouse-2: Next -mouse-3: Stop debugging" - edebug-execution-mode) - 'mouse-face 'doom-modeline-highlight - 'local-map (let ((map (make-sparse-keymap))) - (define-key map [mode-line mouse-1] - #'edebug-help) - (define-key map [mode-line mouse-2] - #'edebug-next-mode) - (define-key map [mode-line mouse-3] - #'edebug-stop) - map)))) - -(defsubst doom-modeline--debug-on-error () - "The current `debug-on-error' state." - (when debug-on-error - (propertize (doom-modeline-debug-icon 'doom-modeline-urgent) - 'help-echo "Debug on Error -mouse-1: Toggle Debug on Error" - 'mouse-face 'doom-modeline-highlight - 'local-map (make-mode-line-mouse-map 'mouse-1 #'toggle-debug-on-error)))) - -(defsubst doom-modeline--debug-on-quit () - "The current `debug-on-quit' state." - (when debug-on-quit - (propertize (doom-modeline-debug-icon 'doom-modeline-warning) - 'help-echo "Debug on Quit -mouse-1: Toggle Debug on Quit" - 'mouse-face 'doom-modeline-highlight - 'local-map (make-mode-line-mouse-map 'mouse-1 #'toggle-debug-on-quit)))) - -(doom-modeline-def-segment debug - "The current debug state." - (when (doom-modeline--segment-visible 'debug) - (let* ((dap doom-modeline--debug-dap) - (edebug (doom-modeline--debug-edebug)) - (on-error (doom-modeline--debug-on-error)) - (on-quit (doom-modeline--debug-on-quit)) - (vsep (doom-modeline-vspc)) - (sep (and (or dap edebug on-error on-quit) (doom-modeline-spc)))) - (concat sep - (and dap (concat dap (and (or edebug on-error on-quit) vsep))) - (and edebug (concat edebug (and (or on-error on-quit) vsep))) - (and on-error (concat on-error (and on-quit vsep))) - on-quit - sep)))) - - -;; -;; PDF pages -;; - -(defvar-local doom-modeline--pdf-pages nil) -(defun doom-modeline-update-pdf-pages () - "Update PDF pages." - (setq doom-modeline--pdf-pages - (format " P%d/%d " - (or (eval `(pdf-view-current-page)) 0) - (pdf-cache-number-of-pages)))) -(add-hook 'pdf-view-change-page-hook #'doom-modeline-update-pdf-pages) - -(doom-modeline-def-segment pdf-pages - "Display PDF pages." - doom-modeline--pdf-pages) - - -;; -;; `mu4e' notifications -;; - -(doom-modeline-def-segment mu4e - "Show notifications of any unread emails in `mu4e'." - (when (and doom-modeline-mu4e - (doom-modeline--segment-visible 'mu4e)) - (let ((sep (doom-modeline-spc)) - (vsep (doom-modeline-vspc)) - (icon (doom-modeline-icon 'mdicon "nf-md-email" "📧" "#" - :face 'doom-modeline-notification))) - (cond ((and (bound-and-true-p mu4e-alert-mode-line) - (numberp mu4e-alert-mode-line) - ;; don't display if the unread mails count is zero - (> mu4e-alert-mode-line 0)) - (concat - sep - (propertize - (concat - icon - vsep - (propertize - (if (> mu4e-alert-mode-line doom-modeline-number-limit) - (format "%d+" doom-modeline-number-limit) - (number-to-string mu4e-alert-mode-line)) - 'face '(:inherit - (doom-modeline-unread-number doom-modeline-notification)))) - 'mouse-face 'doom-modeline-highlight - 'keymap '(mode-line keymap - (mouse-1 . mu4e-alert-view-unread-mails) - (mouse-2 . mu4e-alert-view-unread-mails) - (mouse-3 . mu4e-alert-view-unread-mails)) - 'help-echo (concat (if (= mu4e-alert-mode-line 1) - "You have an unread email" - (format "You have %s unread emails" mu4e-alert-mode-line)) - "\nClick here to view " - (if (= mu4e-alert-mode-line 1) "it" "them"))) - sep)) - ((bound-and-true-p mu4e-modeline-mode) - (concat sep icon vsep - (propertize (mu4e--modeline-string) - 'face 'doom-modeline-notification) - sep)))))) - -(defun doom-modeline-override-mu4e-alert (&rest _) - "Delete `mu4e-alert-mode-line' from global modeline string." - (when (and (featurep 'mu4e-alert) - (bound-and-true-p mu4e-alert-mode-line)) - (if (and doom-modeline-mu4e - (bound-and-true-p doom-modeline-mode)) - ;; Delete original modeline - (progn - (setq global-mode-string - (delete '(:eval mu4e-alert-mode-line) global-mode-string)) - (setq mu4e-alert-modeline-formatter #'identity)) - ;; Recover default settings - (setq mu4e-alert-modeline-formatter #'mu4e-alert-default-mode-line-formatter)))) -(advice-add #'mu4e-alert-enable-mode-line-display - :after #'doom-modeline-override-mu4e-alert) -(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-mu4e-alert) - -(defun doom-modeline-override-mu4e-modeline (&rest _) - "Delete `mu4e-alert-mode-line' from global modeline string." - (when (bound-and-true-p mu4e-modeline-mode) - (if (and doom-modeline-mu4e - (bound-and-true-p doom-modeline-mode)) - ;; Delete original modeline - (setq global-mode-string - (delete mu4e--modeline-item global-mode-string)) - ;; Recover default settings - (add-to-list 'global-mode-string mu4e--modeline-item)))) -(add-hook 'mu4e-modeline-mode-hook #'doom-modeline-override-mu4e-modeline) -(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-mu4e-modeline) - -(doom-modeline-add-variable-watcher - 'doom-modeline-mu4e - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-mu4e val) - (doom-modeline-override-mu4e-alert) - (doom-modeline-override-mu4e-modeline)))) - - -;; -;; `gnus' notifications -;; - -(defvar doom-modeline--gnus-unread-mail 0) -(defvar doom-modeline--gnus-started nil - "Used to determine if gnus has started.") -(defun doom-modeline-update-gnus-status (&rest _) - "Get the total number of unread news of gnus group." - (setq doom-modeline--gnus-unread-mail - (when (and doom-modeline-gnus - doom-modeline--gnus-started) - (let ((total-unread-news-number 0)) - (mapc (lambda (g) - (let* ((group (car g)) - (unread (eval `(gnus-group-unread ,group)))) - (when (and (not (seq-contains-p doom-modeline-gnus-excluded-groups group)) - (numberp unread) - (> unread 0)) - (setq total-unread-news-number (+ total-unread-news-number unread))))) - gnus-newsrc-alist) - total-unread-news-number)))) - -;; Update the modeline after changes have been made -(add-hook 'gnus-group-update-hook #'doom-modeline-update-gnus-status) -(add-hook 'gnus-summary-update-hook #'doom-modeline-update-gnus-status) -(add-hook 'gnus-group-update-group-hook #'doom-modeline-update-gnus-status) -(add-hook 'gnus-after-getting-new-news-hook #'doom-modeline-update-gnus-status) - -;; Only start to listen to gnus when gnus is actually running -(defun doom-modeline-start-gnus-listener () - "Start GNUS listener." - (when (and doom-modeline-gnus - (not doom-modeline--gnus-started)) - (setq doom-modeline--gnus-started t) - ;; Scan gnus in the background if the timer is higher than 0 - (doom-modeline-update-gnus-status) - (if (> doom-modeline-gnus-timer 0) - (gnus-demon-add-handler 'gnus-demon-scan-news doom-modeline-gnus-timer doom-modeline-gnus-idle)))) -(add-hook 'gnus-started-hook #'doom-modeline-start-gnus-listener) - -;; Stop the listener if gnus isn't running -(defun doom-modeline-stop-gnus-listener () - "Stop GNUS listener." - (setq doom-modeline--gnus-started nil)) -(add-hook 'gnus-exit-gnus-hook #'doom-modeline-stop-gnus-listener) - -(doom-modeline-def-segment gnus - "Show notifications of any unread emails in `gnus'." - (when (and (doom-modeline--segment-visible 'gnus) - doom-modeline-gnus - doom-modeline--gnus-started - ;; Don't display if the unread mails count is zero - (numberp doom-modeline--gnus-unread-mail) - (> doom-modeline--gnus-unread-mail 0)) - (let ((sep (doom-modeline-spc)) - (vsep (doom-modeline-vspc))) - (concat - sep - (propertize - (concat - (doom-modeline-icon 'mdicon "nf-md-email" "📧" "#" - :face 'doom-modeline-notification) - vsep - (propertize - (if (> doom-modeline--gnus-unread-mail doom-modeline-number-limit) - (format "%d+" doom-modeline-number-limit) - (number-to-string doom-modeline--gnus-unread-mail)) - 'face '(:inherit - (doom-modeline-unread-number doom-modeline-notification)))) - 'mouse-face 'doom-modeline-highlight - 'help-echo (if (= doom-modeline--gnus-unread-mail 1) - "You have an unread email" - (format "You have %s unread emails" doom-modeline--gnus-unread-mail))) - sep)))) - - -;; -;; IRC notifications -;; - -(defun doom-modeline--shorten-irc (name) - "Wrapper for `tracking-shorten' and `erc-track-shorten-function' with NAME. - -One key difference is that when `tracking-shorten' and -`erc-track-shorten-function' returns nil we will instead return the original -value of name. This is necessary in cases where the user has stylized the name -to be an icon and we don't want to remove that so we just return the original." - (or (and (boundp 'tracking-shorten) - (car (tracking-shorten (list name)))) - (and (boundp 'erc-track-shorten-function) - (functionp erc-track-shorten-function) - (car (funcall erc-track-shorten-function (list name)))) - (and (boundp 'rcirc-short-buffer-name) - (rcirc-short-buffer-name name)) - name)) - -(defun doom-modeline--tracking-buffers (buffers) - "Logic to convert some irc BUFFERS to their font-awesome icon." - (mapconcat - (lambda (b) - (propertize - (doom-modeline--shorten-irc (funcall doom-modeline-irc-stylize b)) - 'face '(:inherit (doom-modeline-unread-number doom-modeline-notification)) - 'help-echo (format "IRC Notification: %s\nmouse-1: Switch to buffer" b) - 'mouse-face 'doom-modeline-highlight - 'local-map (make-mode-line-mouse-map - 'mouse-1 - (lambda () - (interactive) - (when (buffer-live-p (get-buffer b)) - (switch-to-buffer b)))))) - buffers - (doom-modeline-vspc))) - -(defun doom-modeline--circe-p () - "Check if `circe' is in use." - (boundp 'tracking-mode-line-buffers)) - -(defun doom-modeline--erc-p () - "Check if `erc' is in use." - (boundp 'erc-modified-channels-alist)) - -(defun doom-modeline--rcirc-p () - "Check if `rcirc' is in use." - (bound-and-true-p rcirc-track-minor-mode)) - -(defun doom-modeline--get-buffers () - "Gets the buffers that have activity." - (cond - ((doom-modeline--circe-p) - tracking-buffers) - ((doom-modeline--erc-p) - (mapcar (lambda (l) - (buffer-name (car l))) - erc-modified-channels-alist)) - ((doom-modeline--rcirc-p) - (mapcar (lambda (b) - (buffer-name b)) - rcirc-activity)))) - -;; Create a modeline segment that contains all the irc tracked buffers -(doom-modeline-def-segment irc-buffers - "The list of shortened, unread irc buffers." - (when (and doom-modeline-irc - (doom-modeline--segment-visible 'irc-buffers)) - (let* ((buffers (doom-modeline--get-buffers)) - (number (length buffers)) - (sep (doom-modeline-spc))) - (when (> number 0) - (concat - sep - (doom-modeline--tracking-buffers buffers) - sep))))) - -(doom-modeline-def-segment irc - "A notification icon for any unread irc buffer." - (when (and doom-modeline-irc - (doom-modeline--segment-visible 'irc)) - (let* ((buffers (doom-modeline--get-buffers)) - (number (length buffers)) - (sep (doom-modeline-spc)) - (vsep (doom-modeline-vspc))) - (when (> number 0) - (concat - sep - - (propertize (concat - (doom-modeline-icon 'mdicon "nf-md-message_processing" "🗊" "#" - :face 'doom-modeline-notification) - vsep - ;; Display the number of unread buffers - (propertize (number-to-string number) - 'face '(:inherit - (doom-modeline-unread-number - doom-modeline-notification)))) - 'help-echo (format "IRC Notifications: %s\n%s" - (mapconcat - (lambda (b) (funcall doom-modeline-irc-stylize b)) - buffers - ", ") - (cond - ((doom-modeline--circe-p) - "mouse-1: Switch to previous unread buffer -mouse-3: Switch to next unread buffer") - ((doom-modeline--erc-p) - "mouse-1: Switch to buffer -mouse-3: Switch to next unread buffer") - ((doom-modeline--rcirc-p) - "mouse-1: Switch to server buffer -mouse-3: Switch to next unread buffer"))) - 'mouse-face 'doom-modeline-highlight - 'local-map (let ((map (make-sparse-keymap))) - (cond - ((doom-modeline--circe-p) - (define-key map [mode-line mouse-1] - #'tracking-previous-buffer) - (define-key map [mode-line mouse-3] - #'tracking-next-buffer)) - ((doom-modeline--erc-p) - (define-key map [mode-line mouse-1] - #'erc-switch-to-buffer) - (define-key map [mode-line mouse-3] - #'erc-track-switch-buffer)) - ((doom-modeline--rcirc-p) - (define-key map [mode-line mouse-1] - #'rcirc-switch-to-server-buffer) - (define-key map [mode-line mouse-3] - #'rcirc-next-active-buffer))) - map)) - - ;; Display the unread irc buffers as well - (when doom-modeline-irc-buffers - (concat sep (doom-modeline--tracking-buffers buffers))) - - sep))))) - -(defun doom-modeline-override-rcirc () - "Override default `rcirc' mode-line." - (if (and doom-modeline-irc - (bound-and-true-p doom-modeline-mode)) - (setq global-mode-string - (delq 'rcirc-activity-string global-mode-string)) - (when (and rcirc-track-minor-mode - (not (memq 'rcirc-activity-string global-mode-string))) - (setq global-mode-string - (append global-mode-string '(rcirc-activity-string)))))) -(add-hook 'rcirc-track-minor-mode-hook #'doom-modeline-override-rcirc) -(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-rcirc) - -(doom-modeline-add-variable-watcher - 'doom-modeline-irc - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-irc val) - (doom-modeline-override-rcirc)))) - - -;; -;; Battery status -;; - -(defun doom-modeline-battery-icon (icon unicode text face) - "Displays the battery ICON with FACE. - -UNICODE and TEXT are fallbacks. -Uses `nerd-icons-mdicon' to fetch the icon." - (doom-modeline-icon 'mdicon icon unicode text :face face)) - -(defvar doom-modeline--battery-status nil) -(defun doom-modeline-update-battery-status () - "Update battery status." - (setq doom-modeline--battery-status - (when (and doom-modeline-battery - (bound-and-true-p display-battery-mode)) - (let* ((data (and battery-status-function - (functionp battery-status-function) - (funcall battery-status-function))) - (status (cdr (assoc ?L data))) - (charging? (or (string-equal "AC" status) - (string-equal "on-line" status))) - (percentage (car (read-from-string (or (cdr (assq ?p data)) "ERR")))) - (valid-percentage? (and (numberp percentage) - (>= percentage 0) - (<= percentage battery-mode-line-limit))) - (face (if valid-percentage? - (cond (charging? 'doom-modeline-battery-charging) - ((< percentage battery-load-critical) 'doom-modeline-battery-critical) - ((< percentage 25) 'doom-modeline-battery-warning) - ((< percentage 95) 'doom-modeline-battery-normal) - (t 'doom-modeline-battery-full)) - 'doom-modeline-battery-error)) - (icon (if valid-percentage? - (cond - ((>= percentage 100) - (doom-modeline-battery-icon (if charging? - "nf-md-battery_charging_100" - "nf-md-battery") - "🔋" "-" face)) - ((>= percentage 90) - (doom-modeline-battery-icon (if charging? - "nf-md-battery_charging_90" - "nf-md-battery_90") - "🔋" "-" face)) - ((>= percentage 80) - (doom-modeline-battery-icon (if charging? - "nf-md-battery_charging_80" - "nf-md-battery_80") - "🔋" "-" face)) - ((>= percentage 70) - (doom-modeline-battery-icon (if charging? - "nf-md-battery_charging_70" - "nf-md-battery_70") - "🔋" "-" face)) - ((>= percentage 60) - (doom-modeline-battery-icon (if charging? - "nf-md-battery_charging_60" - "nf-md-battery_60") - "🔋" "-" face)) - ((>= percentage 50) - (doom-modeline-battery-icon (if charging? - "nf-md-battery_charging_50" - "nf-md-battery_50") - "🔋" "-" face)) - ((>= percentage 40) - (doom-modeline-battery-icon (if charging? - "nf-md-battery_charging_40" - "nf-md-battery_40") - "🔋" "-" face)) - ((>= percentage 30) - (doom-modeline-battery-icon (if charging? - "nf-md-battery_charging_30" - "nf-md-battery_30") - "🔋" "-" face)) - ((>= percentage 20) - (doom-modeline-battery-icon (if charging? - "nf-md-battery_charging_20" - "nf-md-battery_20") - "🔋" "-" face)) - ((>= percentage 10) - (doom-modeline-battery-icon (if charging? - "nf-md-battery_charging_10" - "nf-md-battery_10") - "🪫" "-" face)) - (t (doom-modeline-battery-icon (if charging? - "nf-md-battery_charging_outline" - "nf-md-battery_outline") - "🪫" "!" face))) - (doom-modeline-battery-icon "nf-md-battery_alert" "⚠" "N/A" face))) - (text (if valid-percentage? (format "%d%s" percentage "%%") "")) - (help-echo (if (and battery-echo-area-format data valid-percentage?) - (battery-format battery-echo-area-format data) - "Battery status not available"))) - (cons (propertize icon 'help-echo help-echo) - (propertize text 'face face 'help-echo help-echo)))))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-icon - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-icon val) - (doom-modeline-update-battery-status)))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-unicode-fallback - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-unicode-fallback val) - (doom-modeline-update-battery-status)))) - -(doom-modeline-def-segment battery - "Display battery status." - (when (and doom-modeline-battery - (bound-and-true-p display-battery-mode) - (doom-modeline--segment-visible 'battery)) - (let ((sep (doom-modeline-spc)) - (vsep (doom-modeline-vspc))) - (concat sep - (car doom-modeline--battery-status) - vsep - (cdr doom-modeline--battery-status) - sep)))) - -(defun doom-modeline-override-battery () - "Override default battery mode-line." - (if (and doom-modeline-battery - (bound-and-true-p doom-modeline-mode)) - (progn - (advice-add #'battery-update :override #'doom-modeline-update-battery-status) - (setq global-mode-string - (delq 'battery-mode-line-string global-mode-string)) - (and (bound-and-true-p display-battery-mode) (battery-update))) - (progn - (advice-remove #'battery-update #'doom-modeline-update-battery-status) - (when (and display-battery-mode battery-status-function battery-mode-line-format - (not (memq 'battery-mode-line-string global-mode-string))) - (setq global-mode-string - (append global-mode-string '(battery-mode-line-string))))))) -(add-hook 'display-battery-mode-hook #'doom-modeline-override-battery) -(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-battery) - -(doom-modeline-add-variable-watcher - 'doom-modeline-battery - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-battery val) - (doom-modeline-override-battery)))) - - -;; -;; Package information -;; - -(doom-modeline-def-segment package - "Show package information via `paradox'." - (concat - (doom-modeline-display-text - (format-mode-line 'mode-line-front-space)) - - (when (and doom-modeline-icon doom-modeline-major-mode-icon) - (concat - (doom-modeline-spc) - (doom-modeline-icon 'faicon "nf-fa-archive" nil nil - :face (doom-modeline-face - (if doom-modeline-major-mode-color-icon - 'nerd-icons-silver - 'mode-line))))) - (doom-modeline-display-text - (format-mode-line 'mode-line-buffer-identification)))) - - -;; -;; Helm -;; - -(defvar doom-modeline--helm-buffer-ids - '(("*helm*" . "HELM") - ("*helm M-x*" . "HELM M-x") - ("*swiper*" . "SWIPER") - ("*Projectile Perspectives*" . "HELM Projectile Perspectives") - ("*Projectile Layouts*" . "HELM Projectile Layouts") - ("*helm-ag*" . (lambda () - (format "HELM Ag: Using %s" - (car (split-string helm-ag-base-command)))))) - "Alist of custom helm buffer names to use. -The cdr can also be a function that returns a name to use.") - -(doom-modeline-def-segment helm-buffer-id - "Helm session identifier." - (when (bound-and-true-p helm-alive-p) - (let ((sep (doom-modeline-spc))) - (concat - sep - (when doom-modeline-icon - (concat - (doom-modeline-icon 'sucicon "nf-custom-emacs" nil nil - :face (doom-modeline-face - (and doom-modeline-major-mode-color-icon - 'nerd-icons-blue))) - sep)) - (propertize - (let ((custom (cdr (assoc (buffer-name) doom-modeline--helm-buffer-ids))) - (case-fold-search t) - (name (replace-regexp-in-string "-" " " (buffer-name)))) - (cond ((stringp custom) custom) - ((functionp custom) (funcall custom)) - (t - (string-match "\\*helm:? \\(mode \\)?\\([^\\*]+\\)\\*" name) - (concat "HELM " (capitalize (match-string 2 name)))))) - 'face (doom-modeline-face 'doom-modeline-buffer-file)) - sep)))) - -(doom-modeline-def-segment helm-number - "Number of helm candidates." - (when (bound-and-true-p helm-alive-p) - (concat - (propertize (format " %d/%d" - (helm-candidate-number-at-point) - (helm-get-candidate-number t)) - 'face (doom-modeline-face 'doom-modeline-buffer-path)) - (propertize (format " (%d total) " (helm-get-candidate-number)) - 'face (doom-modeline-face 'doom-modeline-info))))) - -(doom-modeline-def-segment helm-help - "Helm keybindings help." - (when (bound-and-true-p helm-alive-p) - (mapcar - (lambda (s) - (if (string-prefix-p "\\<" s) - (propertize (substitute-command-keys s) - 'face (doom-modeline-face - 'doom-modeline-buffer-file)) - s)) - '("\\<helm-map>\\[helm-help]" "(help) " - "\\<helm-map>\\[helm-select-action]" "(actions) " - "\\<helm-map>\\[helm-maybe-exit-minibuffer]/F1/F2..." "(action) ")))) - -(doom-modeline-def-segment helm-prefix-argument - "Helm prefix argument." - (when (and (bound-and-true-p helm-alive-p) - helm--mode-line-display-prefarg) - (let ((arg (prefix-numeric-value (or prefix-arg current-prefix-arg)))) - (unless (= arg 1) - (propertize (format "C-u %s" arg) - 'face (doom-modeline-face 'doom-modeline-info)))))) - -(defvar doom-modeline--helm-current-source nil - "The currently active helm source.") -(doom-modeline-def-segment helm-follow - "Helm follow indicator." - (and (bound-and-true-p helm-alive-p) - doom-modeline--helm-current-source - (eq 1 (cdr (assq 'follow doom-modeline--helm-current-source))) - "HF")) - -;; -;; Git timemachine -;; - -(doom-modeline-def-segment git-timemachine - (concat - (doom-modeline-spc) - (doom-modeline--buffer-mode-icon) - (doom-modeline--buffer-state-icon) - (propertize - "*%b*" - 'face (doom-modeline-face 'doom-modeline-buffer-timemachine)))) - -;; -;; Markdown/Org preview -;; - -(doom-modeline-def-segment grip - (when (bound-and-true-p grip-mode) - (let ((sep (doom-modeline-spc))) - (concat - sep - (let ((face (doom-modeline-face - (if grip--process - (pcase (process-status grip--process) - ('run 'doom-modeline-info) - ('exit 'doom-modeline-warning) - (_ 'doom-modeline-urgent)) - 'doom-modeline-urgent)))) - (propertize - (doom-modeline-icon 'codicon "nf-cod-open_preview" "🗐" "@" :face face) - 'help-echo (format "Preview on %s -mouse-1: Preview in browser -mouse-2: Stop preview -mouse-3: Restart preview" - (grip--preview-url)) - 'mouse-face 'doom-modeline-highlight - 'local-map (let ((map (make-sparse-keymap))) - (define-key map [mode-line mouse-1] - #'grip-browse-preview) - (define-key map [mode-line mouse-2] - #'grip-stop-preview) - (define-key map [mode-line mouse-3] - #'grip-restart-preview) - map))) - sep)))) - -;; -;; Follow mode -;; - -(doom-modeline-def-segment follow - (when (bound-and-true-p follow-mode) - (let* ((windows (follow-all-followers)) - (nwindows (length windows)) - (nfollowing (- (length (memq (selected-window) windows)) 1))) - (concat - (doom-modeline-spc) - (propertize (format "Follow %d/%d" (- nwindows nfollowing) nwindows) - 'face 'doom-modeline-buffer-minor-mode))))) - -;; -;; Display time -;; - -(defconst doom-modeline--clock-hour-hand-ratio 0.45 - "Length of the hour hand as a proportion of the radius.") - -(defconst doom-modeline--clock-minute-hand-ratio 0.7 - "Length of the minute hand as a proportion of the radius.") - -(defun doom-modeline--create-clock-svg (hour minute radius color) - "Construct an SVG clock showing the time HOUR:MINUTE. -The clock will be of the specified RADIUS and COLOR." - (let ((thickness-factor (image-compute-scaling-factor 'auto)) - (hour-x (* radius (sin (* (- 6 hour (/ minute 60.0)) (/ float-pi 6))) - doom-modeline--clock-hour-hand-ratio)) - (hour-y (* radius (cos (* (- 6 hour (/ minute 60.0)) (/ float-pi 6))) - doom-modeline--clock-hour-hand-ratio)) - (minute-x (* radius (sin (* (- 30 minute) (/ float-pi 30))) - doom-modeline--clock-minute-hand-ratio)) - (minute-y (* radius (cos (* (- 30 minute) (/ float-pi 30))) - doom-modeline--clock-minute-hand-ratio)) - (svg (svg-create (* 2 radius) (* 2 radius) :stroke color))) - (svg-circle svg radius radius (- radius thickness-factor) - :fill "none" :stroke-width (* 2 thickness-factor)) - (svg-circle svg radius radius thickness-factor - :fill color :stroke "none") - (svg-line svg radius radius (+ radius hour-x) (+ radius hour-y) - :stroke-width (* 2 thickness-factor)) - (svg-line svg radius radius (+ radius minute-x) (+ radius minute-y) - :stroke-width (* 1.5 thickness-factor)) - svg)) - -(defvar doom-modeline--clock-cache nil - "The last result of `doom-modeline--generate-clock'.") - -(defun doom-modeline--generate-clock () - "Return a string containing the current time as an analogue clock svg. -When the svg library is not available, return nil." - (cdr - (or (and (equal (truncate (float-time) - (* doom-modeline-time-clock-minute-resolution 60)) - (car doom-modeline--clock-cache)) - doom-modeline--clock-cache) - (and (require 'svg nil t) - (setq doom-modeline--clock-cache - (cons (truncate (float-time) - (* doom-modeline-time-clock-minute-resolution 60)) - (propertize - " " - 'display - (svg-image - (doom-modeline--create-clock-svg - (string-to-number (format-time-string "%-I")) ; hour - (* (truncate (string-to-number (format-time-string "%-M")) - doom-modeline-time-clock-minute-resolution) - doom-modeline-time-clock-minute-resolution) ; minute - (if (integerp doom-modeline-time-clock-size) ; radius - doom-modeline-time-clock-size - (* doom-modeline-height 0.5 doom-modeline-time-clock-size)) - "currentColor") - :scale 1 :ascent 'center) - 'face 'doom-modeline-time - 'help-echo (lambda (_window _object _pos) - (format-time-string "%c"))))))))) - -(defun doom-modeline-time-icon () - "Displays the time icon." - (or (and doom-modeline-time-live-icon - doom-modeline-time-analogue-clock - (display-graphic-p) - (doom-modeline--generate-clock)) - (doom-modeline-icon - 'mdicon - (if doom-modeline-time-live-icon - (pcase (% (caddr (decode-time)) 12) - (0 "nf-md-clock_time_twelve_outline") - (1 "nf-md-clock_time_one_outline") - (2 "nf-md-clock_time_two_outline") - (3 "nf-md-clock_time_three_outline") - (4 "nf-md-clock_time_four_outline") - (5 "nf-md-clock_time_five_outline") - (6 "nf-md-clock_time_six_outline") - (7 "nf-md-clock_time_seven_outline") - (8 "nf-md-clock_time_eight_outline") - (9 "nf-md-clock_time_nine_outline") - (10 "nf-md-clock_time_ten_outline") - (11 "nf-md-clock_time_eleven_outline")) - "nf-md-clock_outline") - "⏰" - "" - :face '(:inherit doom-modeline-time :weight normal)))) - -(doom-modeline-def-segment time - (when (and doom-modeline-time - (bound-and-true-p display-time-mode) - (doom-modeline--segment-visible 'time)) - (concat - (doom-modeline-spc) - (when doom-modeline-time-icon - (concat - (doom-modeline-time-icon) - (and (or doom-modeline-icon doom-modeline-unicode-fallback) - (doom-modeline-vspc)))) - (propertize display-time-string - 'face (doom-modeline-face 'doom-modeline-time))))) - -(defun doom-modeline-override-time () - "Override default `display-time' mode-line." - (or global-mode-string (setq global-mode-string '(""))) - (if (and doom-modeline-time - (bound-and-true-p doom-modeline-mode)) - (setq global-mode-string (delq 'display-time-string global-mode-string)) - (setq global-mode-string (append global-mode-string '(display-time-string))))) -(add-hook 'display-time-mode-hook #'doom-modeline-override-time) -(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-time) - -(doom-modeline-add-variable-watcher - 'doom-modeline-time - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-time val) - (doom-modeline-override-time)))) - -;; -;; Compilation -;; - -(doom-modeline-def-segment compilation - (and (bound-and-true-p compilation-in-progress) - (propertize "[Compiling] " - 'face (doom-modeline-face 'doom-modeline-compilation) - 'help-echo "Compiling; mouse-2: Goto Buffer" - 'mouse-face 'doom-modeline-highlight - 'local-map - (make-mode-line-mouse-map - 'mouse-2 - #'compilation-goto-in-progress-buffer)))) - -;; -;; Eldoc -;; - -(doom-modeline-def-segment eldoc - (and (bound-and-true-p eldoc-mode) - '(eldoc-mode-line-string - (" " eldoc-mode-line-string " ")))) - -(defun doom-modeline-eldoc-minibuffer-message (format-string &rest args) - "Display message specified by FORMAT-STRING and ARGS on the mode-line as needed. -This function displays the message produced by formatting ARGS -with FORMAT-STRING on the mode line when the current buffer is a minibuffer. -Otherwise, it displays the message like `message' would." - (if (minibufferp) - (progn - (add-hook 'minibuffer-exit-hook - (lambda () (setq eldoc-mode-line-string nil - ;; https://debbugs.gnu.org/16920 - eldoc-last-message nil)) - nil t) - (with-current-buffer - (window-buffer - (or (window-in-direction 'above (minibuffer-window)) - (minibuffer-selected-window) - (get-largest-window))) - (setq eldoc-mode-line-string - (when (stringp format-string) - (apply #'format-message format-string args))) - (force-mode-line-update))) - (apply #'message format-string args))) - -;; -;; Kubernetes -;; - -(doom-modeline-def-segment k8s - (when (and (bound-and-true-p kele-mode) (doom-modeline--segment-visible 'k8s)) - (let* ((ctx (kele-current-context-name :wait nil)) - (ns (kele-current-namespace :wait nil)) - (icon (doom-modeline-icon 'mdicon "nf-md-kubernetes" "K8s:" "K8s:")) - (sep (doom-modeline-spc)) - (help-msg (let ((msgs (list (format "Current context: %s" ctx)))) - (when ns - (setq msgs (append msgs (list (format "Current namespace: %s" ns))))) - (string-join msgs "\n")))) - (propertize (concat - icon sep ctx - (when (and doom-modeline-k8s-show-namespace ns) (format "(%s)" ns)) - sep) - 'local-map (let ((map (make-sparse-keymap))) - (define-key map [mode-line down-mouse-1] kele-menu-map) - map) - 'mouse-face 'doom-modeline-highlight - 'help-echo help-msg)))) - -(provide 'doom-modeline-segments) - -;;; doom-modeline-segments.el ends here diff --git a/emacs/elpa/doom-modeline-20240510.144/doom-modeline-segments.elc b/emacs/elpa/doom-modeline-20240510.144/doom-modeline-segments.elc Binary files differ. diff --git a/emacs/elpa/doom-modeline-20240510.144/doom-modeline-autoloads.el b/emacs/elpa/doom-modeline-20240605.628/doom-modeline-autoloads.el diff --git a/emacs/elpa/doom-modeline-20240510.144/doom-modeline-core.el b/emacs/elpa/doom-modeline-20240605.628/doom-modeline-core.el diff --git a/emacs/elpa/doom-modeline-20240510.144/doom-modeline-core.elc b/emacs/elpa/doom-modeline-20240605.628/doom-modeline-core.elc Binary files differ. diff --git a/emacs/elpa/doom-modeline-20240510.144/doom-modeline-env.el b/emacs/elpa/doom-modeline-20240605.628/doom-modeline-env.el diff --git a/emacs/elpa/doom-modeline-20240510.144/doom-modeline-env.elc b/emacs/elpa/doom-modeline-20240605.628/doom-modeline-env.elc Binary files differ. diff --git a/emacs/elpa/doom-modeline-20240605.628/doom-modeline-pkg.el b/emacs/elpa/doom-modeline-20240605.628/doom-modeline-pkg.el @@ -0,0 +1,17 @@ +(define-package "doom-modeline" "20240605.628" "A minimal and modern mode-line" + '((emacs "25.1") + (compat "29.1.4.5") + (nerd-icons "0.1.0") + (shrink-path "0.3.1")) + :commit "11ae6c193cd9cb8d7ff7996058e6df2c0d1e408b" :authors + '(("Vincent Zhang" . "seagle0128@gmail.com")) + :maintainers + '(("Vincent Zhang" . "seagle0128@gmail.com")) + :maintainer + '("Vincent Zhang" . "seagle0128@gmail.com") + :keywords + '("faces" "mode-line") + :url "https://github.com/seagle0128/doom-modeline") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs/elpa/doom-modeline-20240605.628/doom-modeline-segments.el b/emacs/elpa/doom-modeline-20240605.628/doom-modeline-segments.el @@ -0,0 +1,3243 @@ +;;; doom-modeline-segments.el --- The segments for doom-modeline -*- lexical-binding: t; -*- + +;; Copyright (C) 2018-2024 Vincent Zhang + +;; This file is not part of GNU Emacs. + +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. +;; + +;;; Commentary: +;; +;; The segments for doom-modeline. +;; Use `doom-modeline-def-segment' to create a new segment. +;; + +;;; Code: + +(require 'doom-modeline-core) +(require 'doom-modeline-env) +(eval-when-compile + (require 'cl-lib) + (require 'seq) + (require 'subr-x)) + + +;; +;; Externals +;; + +(defvar Info-current-file) +(defvar Info-current-node) +(defvar Info-mode-line-node-keymap) +(defvar anzu--cached-count) +(defvar anzu--current-position) +(defvar anzu--overflow-p) +(defvar anzu--state) +(defvar anzu--total-matched) +(defvar anzu-cons-mode-line-p) +(defvar aw-keys) +(defvar battery-echo-area-format) +(defvar battery-load-critical) +(defvar battery-mode-line-format) +(defvar battery-mode-line-limit) +(defvar battery-status-function) +(defvar boon-command-state) +(defvar boon-insert-state) +(defvar boon-off-state) +(defvar boon-special-state) +(defvar display-time-string) +(defvar edebug-execution-mode) +(defvar eglot--managed-mode) +(defvar erc-modified-channels-alist) +(defvar evil-ex-active-highlights-alist) +(defvar evil-ex-argument) +(defvar evil-ex-range) +(defvar evil-mc-frozen) +(defvar evil-state) +(defvar evil-visual-beginning) +(defvar evil-visual-end) +(defvar evil-visual-selection) +(defvar flycheck--automatically-enabled-checkers) +(defvar flycheck-current-errors) +(defvar flycheck-mode-menu-map) +(defvar flymake--mode-line-format) +(defvar flymake--state) +(defvar flymake-menu) +(defvar gnus-newsrc-alist) +(defvar gnus-newsrc-hashtb) +(defvar grip--process) +(defvar helm--mode-line-display-prefarg) +(defvar iedit-occurrences-overlays) +(defvar kele-menu-map) +(defvar meow--indicator) +(defvar minions-mode-line-lighter) +(defvar minions-mode-line-minor-modes-map) +(defvar mlscroll-minimum-current-width) +(defvar mlscroll-right-align) +(defvar mu4e--modeline-item) +(defvar mu4e-alert-mode-line) +(defvar mu4e-alert-modeline-formatter) +(defvar mu4e-modeline-mode) +(defvar nyan-minimum-window-width) +(defvar objed--obj-state) +(defvar objed--object) +(defvar objed-modeline-setup-func) +(defvar persp-nil-name) +(defvar phi-replace--mode-line-format) +(defvar phi-search--overlays) +(defvar phi-search--selection) +(defvar phi-search-mode-line-format) +(defvar poke-line-minimum-window-width) +(defvar rcirc-activity) +(defvar sml-modeline-len) +(defvar symbol-overlay-keywords-alist) +(defvar symbol-overlay-temp-symbol) +(defvar text-scale-mode-amount) +(defvar tracking-buffers) +(defvar winum-auto-setup-mode-line) +(defvar xah-fly-insert-state-p) + +(declare-function anzu--reset-status "ext:anzu") +(declare-function anzu--where-is-here "ext:anzu") +(declare-function async-inject-variables "ext:async") +(declare-function async-start "ext:async") +(declare-function avy-traverse "ext:avy") +(declare-function avy-tree "ext:avy") +(declare-function aw-update "ext:ace-window") +(declare-function aw-window-list "ext:ace-window") +(declare-function battery-format "battery") +(declare-function battery-update "battery") +(declare-function boon-modeline-string "ext:boon") +(declare-function boon-state-string "ext:boon") +(declare-function cider--connection-info "ext:cider") +(declare-function cider-connected-p "ext:cider") +(declare-function cider-current-repl "ext:cider") +(declare-function cider-jack-in "ext:cider") +(declare-function cider-quit "ext:cider") +(declare-function citre-mode "ext:citre-basic-tools") +(declare-function compilation-goto-in-progress-buffer "compile") +(declare-function dap--cur-session "ext:dap-mode") +(declare-function dap--debug-session-name "ext:dap-mode") +(declare-function dap--debug-session-state "ext:dap-mode") +(declare-function dap--session-running "ext:dap-mode") +(declare-function dap-debug-recent "ext:dap-mode") +(declare-function dap-disconnect "ext:dap-mode") +(declare-function dap-hydra "ext:dap-hydra") +(declare-function edebug-help "edebug") +(declare-function edebug-next-mode "edebug") +(declare-function edebug-stop "edebug") +(declare-function eglot "ext:eglot") +(declare-function eglot--major-modes "ext:eglot" t t) +(declare-function eglot--project-nickname "ext:eglot" t t) +(declare-function eglot-clear-status "ext:eglot") +(declare-function eglot-current-server "ext:eglot") +(declare-function eglot-events-buffer "ext:eglot") +(declare-function eglot-forget-pending-continuations "ext:eglot") +(declare-function eglot-managed-p "ext:glot") +(declare-function eglot-reconnect "ext:eglot") +(declare-function eglot-shutdown "ext:eglot") +(declare-function eglot-stderr-buffer "ext:eglot") +(declare-function erc-switch-to-buffer "erc") +(declare-function erc-track-switch-buffer "erc-track") +(declare-function evil-delimited-arguments "ext:evil-common") +(declare-function evil-emacs-state-p "ext:evil-states" t t) +(declare-function evil-force-normal-state "ext:evil-commands" t t) +(declare-function evil-insert-state-p "ext:evil-states" t t) +(declare-function evil-motion-state-p "ext:evil-states" t t) +(declare-function evil-normal-state-p "ext:evil-states" t t) +(declare-function evil-operator-state-p "ext:evil-states" t t) +(declare-function evil-replace-state-p "ext:evil-states" t t) +(declare-function evil-state-property "ext:evil-common") +(declare-function evil-visual-state-p "ext:evil-states" t t) +(declare-function eyebrowse--get "ext:eyebrowse") +(declare-function face-remap-remove-relative "face-remap") +(declare-function fancy-narrow-active-p "ext:fancy-narrow") +(declare-function flycheck-buffer "ext:flycheck") +(declare-function flycheck-count-errors "ext:flycheck") +(declare-function flycheck-error-level-compilation-level "ext:flycheck") +(declare-function flycheck-list-errors "ext:flycheck") +(declare-function flycheck-next-error "ext:flycheck") +(declare-function flycheck-previous-error "ext:flycheck") +(declare-function flymake--diag-type "ext:flymake" t t) +(declare-function flymake--handle-report "ext:flymake") +(declare-function flymake--lookup-type-property "ext:flymake") +(declare-function flymake--state-diags "ext:flymake" t t) +(declare-function flymake-disabled-backends "ext:flymake") +(declare-function flymake-goto-next-error "ext:flymake") +(declare-function flymake-goto-prev-error "ext:flymake") +(declare-function flymake-reporting-backends "ext:flymake") +(declare-function flymake-running-backends "ext:flymake") +(declare-function flymake-show-buffer-diagnostics "ext:flymake") +(declare-function flymake-show-buffer-diagnostics "ext:flymake") +(declare-function flymake-start "ext:flymake") +(declare-function follow-all-followers "follow") +(declare-function gnus-demon-add-handler "gnus-demon") +(declare-function grip--preview-url "ext:grip-mode") +(declare-function grip-browse-preview "ext:grip-mode") +(declare-function grip-restart-preview "ext:grip-mode") +(declare-function grip-stop-preview "ext:grip-mode") +(declare-function helm-candidate-number-at-point "ext:helm-core") +(declare-function helm-get-candidate-number "ext:helm-core") +(declare-function iedit-find-current-occurrence-overlay "ext:iedit-lib") +(declare-function iedit-prev-occurrence "ext:iedit-lib") +(declare-function image-get-display-property "image-mode") +(declare-function jsonrpc--request-continuations "ext:jsonrpc" t t) +(declare-function jsonrpc-last-error "ext:jsonrpc" t t) +(declare-function kele-current-context-name "ext:kele") +(declare-function kele-current-namespace "ext:kele") +(declare-function lsp--workspace-print "ext:lsp-mode") +(declare-function lsp-describe-session "ext:lsp-mode") +(declare-function lsp-workspace-folders-open "ext:lsp-mode") +(declare-function lsp-workspace-restart "ext:lsp-mode") +(declare-function lsp-workspace-shutdown "ext:lsp-mode") +(declare-function lsp-workspaces "ext:lsp-mode") +(declare-function lv-message "ext:lv") +(declare-function mc/num-cursors "ext:multiple-cursors-core") +(declare-function meow--current-state "ext:meow") +(declare-function meow-beacon-mode-p "ext:meow") +(declare-function meow-insert-mode-p "ext:meow") +(declare-function meow-keypad-mode-p "ext:meow") +(declare-function meow-motion-mode-p "ext:meow") +(declare-function meow-normal-mode-p "ext:meow") +(declare-function minions--prominent-modes "ext:minions") +(declare-function mlscroll-mode-line "ext:mlscroll") +(declare-function mu4e--modeline-string "ext:mu4e-modeline") +(declare-function mu4e-alert-default-mode-line-formatter "ext:mu4e-alert") +(declare-function mu4e-alert-enable-mode-line-display "ext:mu4e-alert") +(declare-function nyan-create "ext:nyan-mode") +(declare-function org-edit-src-save "ext:org-src") +(declare-function parrot-create "ext:parrot") +(declare-function pdf-cache-number-of-pages "ext:pdf-cache" t t) +(declare-function persp-add-buffer "ext:persp-mode") +(declare-function persp-contain-buffer-p "ext:persp-mode") +(declare-function persp-switch "ext:persp-mode") +(declare-function phi-search--initialize "ext:phi-search") +(declare-function poke-line-create "ext:poke-line") +(declare-function popup-create "ext:popup") +(declare-function popup-delete "ext:popup") +(declare-function rcirc-next-active-buffer "rcirc") +(declare-function rcirc-short-buffer-name "rcirc") +(declare-function rcirc-switch-to-server-buffer "rcirc") +(declare-function rcirc-window-configuration-change "rcirc") +(declare-function rime--should-enable-p "ext:rime") +(declare-function rime--should-inline-ascii-p "ext:rime") +(declare-function sml-modeline-create "ext:sml-modeline") +(declare-function svg-circle "svg") +(declare-function svg-create "svg") +(declare-function svg-image "svg") +(declare-function svg-line "svg") +(declare-function symbol-overlay-assoc "ext:symbol-overlay") +(declare-function symbol-overlay-get-list "ext:symbol-overlay") +(declare-function symbol-overlay-get-symbol "ext:symbol-overlay") +(declare-function symbol-overlay-rename "ext:symbol-overlay") +(declare-function tab-bar--current-tab "tab-bar") +(declare-function tab-bar--current-tab-index "tab-bar") +(declare-function tracking-next-buffer "ext:tracking") +(declare-function tracking-previous-buffer "ext:tracking") +(declare-function tracking-shorten "ext:tracking") +(declare-function undo-tree-redo-1 "ext:undo-tree") +(declare-function undo-tree-undo-1 "ext:undo-tree") +(declare-function warning-numeric-level "warnings") +(declare-function window-numbering-clear-mode-line "ext:window-numbering") +(declare-function window-numbering-get-number-string "ext:window-numbering") +(declare-function window-numbering-install-mode-line "ext:window-numbering") +(declare-function winum--clear-mode-line "ext:winum") +(declare-function winum--install-mode-line "ext:winum") +(declare-function winum-get-number-string "ext:winum") + + + +;; +;; Buffer information +;; + +(defvar-local doom-modeline--buffer-file-icon nil) +(defun doom-modeline-update-buffer-file-icon (&rest _) + "Update file icon in mode-line." + (setq doom-modeline--buffer-file-icon + (when (and doom-modeline-major-mode-icon + (doom-modeline-icon-displayable-p)) + (let ((icon (doom-modeline-icon-for-buffer))) + (propertize (if (or (null icon) (symbolp icon)) + (doom-modeline-icon 'faicon "nf-fa-file_o" nil nil + :face 'nerd-icons-dsilver) + (doom-modeline-propertize-icon icon)) + 'help-echo (format "Major-mode: %s" (format-mode-line mode-name))))))) +(add-hook 'find-file-hook #'doom-modeline-update-buffer-file-icon) +(add-hook 'after-change-major-mode-hook #'doom-modeline-update-buffer-file-icon) +(add-hook 'clone-indirect-buffer-hook #'doom-modeline-update-buffer-file-icon) + +(doom-modeline-add-variable-watcher + 'doom-modeline-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-update-buffer-file-icon)))))) + +(defun doom-modeline-buffer-file-state-icon (icon unicode text face) + "Displays an ICON of buffer state with FACE. +UNICODE and TEXT are the alternatives if it is not applicable. +Uses `nerd-icons-mdicon' to fetch the icon." + (doom-modeline-icon 'mdicon icon unicode text :face face)) + +(defvar-local doom-modeline--buffer-file-state-icon nil) +(defun doom-modeline-update-buffer-file-state-icon (&rest _) + "Update the buffer or file state in mode-line." + (setq doom-modeline--buffer-file-state-icon + (when doom-modeline-buffer-state-icon + (ignore-errors + (concat + (cond (buffer-read-only + (doom-modeline-buffer-file-state-icon + "nf-md-lock" "🔒" "%1*" + 'doom-modeline-warning)) + ((and buffer-file-name (buffer-modified-p) + doom-modeline-buffer-modification-icon) + (doom-modeline-buffer-file-state-icon + "nf-md-content_save_edit" "💾" "%1*" + 'doom-modeline-warning)) + ((and buffer-file-name + ;; Avoid freezing while connection is lost + (not (file-remote-p buffer-file-name)) + (not (file-exists-p buffer-file-name))) + (doom-modeline-buffer-file-state-icon + "nf-md-cancel" "🚫" "!" + 'doom-modeline-urgent)) + (t "")) + (when (or (buffer-narrowed-p) + (and (bound-and-true-p fancy-narrow-mode) + (fancy-narrow-active-p)) + (bound-and-true-p dired-narrow-mode)) + (doom-modeline-buffer-file-state-icon + "nf-md-unfold_less_horizontal" "↕" "><" + 'doom-modeline-warning))))))) + +(defvar-local doom-modeline--buffer-file-name nil) +(defun doom-modeline-update-buffer-file-name (&rest _) + "Update buffer file name in mode-line." + (setq doom-modeline--buffer-file-name + (ignore-errors + (save-match-data + (if buffer-file-name + (doom-modeline-buffer-file-name) + (propertize "%b" + 'face 'doom-modeline-buffer-file + 'mouse-face 'doom-modeline-highlight + 'help-echo "Buffer name +mouse-1: Previous buffer\nmouse-3: Next buffer" + 'local-map mode-line-buffer-identification-keymap)))))) +(add-hook 'find-file-hook #'doom-modeline-update-buffer-file-name) +(add-hook 'after-save-hook #'doom-modeline-update-buffer-file-name) +(add-hook 'clone-indirect-buffer-hook #'doom-modeline-update-buffer-file-name) +(add-hook 'evil-insert-state-exit-hook #'doom-modeline-update-buffer-file-name) +(add-hook 'Info-selection-hook #'doom-modeline-update-buffer-file-name) +(advice-add #'rename-buffer :after #'doom-modeline-update-buffer-file-name) +(advice-add #'set-visited-file-name :after #'doom-modeline-update-buffer-file-name) +(advice-add #'pop-to-buffer :after #'doom-modeline-update-buffer-file-name) +(advice-add #'popup-create :after #'doom-modeline-update-buffer-file-name) +(advice-add #'popup-delete :after #'doom-modeline-update-buffer-file-name) +;; (advice-add #'primitive-undo :after #'doom-modeline-update-buffer-file-name) +;; (advice-add #'set-buffer-modified-p :after #'doom-modeline-update-buffer-file-name) + +(with-no-warnings + (if (boundp 'after-focus-change-function) + (progn + (advice-add #'handle-switch-frame :after #'doom-modeline-update-buffer-file-name) + (add-function :after after-focus-change-function #'doom-modeline-update-buffer-file-name)) + (progn + (add-hook 'focus-in-hook #'doom-modeline-update-buffer-file-name) + (add-hook 'focus-out-hook #'doom-modeline-update-buffer-file-name)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-buffer-file-name-style + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-buffer-file-name-style val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when buffer-file-name + (doom-modeline-update-buffer-file-name))))))) + +(defsubst doom-modeline--buffer-mode-icon () + "The icon of the current major mode." + (when (and doom-modeline-icon doom-modeline-major-mode-icon) + (when-let ((icon (or doom-modeline--buffer-file-icon + (doom-modeline-update-buffer-file-icon)))) + (unless (string-empty-p icon) + (concat + (if doom-modeline-major-mode-color-icon + (doom-modeline-display-icon icon) + (doom-modeline-propertize-icon + icon + (doom-modeline-face))) + (doom-modeline-vspc)))))) + +(defsubst doom-modeline--buffer-state-icon () + "The icon of the current buffer state." + (when doom-modeline-buffer-state-icon + (when-let ((icon (doom-modeline-update-buffer-file-state-icon))) + (unless (string-empty-p icon) + (concat + (doom-modeline-display-icon icon) + (doom-modeline-vspc)))))) + +(defsubst doom-modeline--buffer-simple-name () + "The buffer simple name." + (propertize "%b" + 'face (doom-modeline-face + (if (and doom-modeline-highlight-modified-buffer-name + (buffer-modified-p)) + 'doom-modeline-buffer-modified + 'doom-modeline-buffer-file)) + 'mouse-face 'doom-modeline-highlight + 'help-echo "Buffer name +mouse-1: Previous buffer\nmouse-3: Next buffer" + 'local-map mode-line-buffer-identification-keymap)) + +(defsubst doom-modeline--buffer-name () + "The current buffer name." + (when doom-modeline-buffer-name + (if (and (not (eq doom-modeline-buffer-file-name-style 'file-name)) + doom-modeline--limited-width-p) + ;; Only display the buffer name if the window is small, and doesn't + ;; need to respect file-name style. + (doom-modeline--buffer-simple-name) + (when-let ((name (or doom-modeline--buffer-file-name + (doom-modeline-update-buffer-file-name)))) + ;; Check if the buffer is modified + (if (and doom-modeline-highlight-modified-buffer-name + (buffer-modified-p)) + (propertize name 'face (doom-modeline-face 'doom-modeline-buffer-modified)) + (doom-modeline-display-text name)))))) + +(doom-modeline-def-segment buffer-info + "Combined information about the current buffer. + +Including the current working directory, the file name, and its state (modified, +read-only or non-existent)." + (concat + (doom-modeline-spc) + (doom-modeline--buffer-mode-icon) + (doom-modeline--buffer-state-icon) + (doom-modeline--buffer-name))) + +(doom-modeline-def-segment buffer-info-simple + "Display only the current buffer's name, but with fontification." + (concat + (doom-modeline-spc) + (doom-modeline--buffer-mode-icon) + (doom-modeline--buffer-state-icon) + (doom-modeline--buffer-simple-name))) + +(doom-modeline-def-segment calc + "Display calculator icons and info." + (concat + (doom-modeline-spc) + (when-let ((icon (doom-modeline-icon 'faicon "nf-fa-calculator" "🖩" ""))) + (concat + (doom-modeline-display-icon icon) + (doom-modeline-vspc))) + (doom-modeline--buffer-simple-name))) + +(doom-modeline-def-segment buffer-default-directory + "Displays `default-directory' with the icon and state. + +This is for special buffers like the scratch buffer where knowing the current +project directory is important." + (let ((face (doom-modeline-face + (if (and buffer-file-name (buffer-modified-p)) + 'doom-modeline-buffer-modified + 'doom-modeline-buffer-path)))) + (concat + (doom-modeline-spc) + (and doom-modeline-major-mode-icon + (concat + (doom-modeline-icon + 'octicon "nf-oct-file_directory_fill" "🖿" "" :face face) + (doom-modeline-vspc))) + (doom-modeline--buffer-state-icon) + (propertize (abbreviate-file-name default-directory) 'face face)))) + +(doom-modeline-def-segment buffer-default-directory-simple + "Displays `default-directory'. + +This is for special buffers like the scratch buffer where knowing the current +project directory is important." + (let ((face (doom-modeline-face 'doom-modeline-buffer-path))) + (concat + (doom-modeline-spc) + (and doom-modeline-major-mode-icon + (concat + (doom-modeline-icon + 'octicon "nf-oct-file_directory_fill" "🖿" "" :face face) + (doom-modeline-vspc))) + (propertize (abbreviate-file-name default-directory) 'face face)))) + + +;; +;; Encoding +;; + +(doom-modeline-def-segment buffer-encoding + "Displays the eol and the encoding style of the buffer." + (when doom-modeline-buffer-encoding + (let ((sep (doom-modeline-spc)) + (face (doom-modeline-face)) + (mouse-face 'doom-modeline-highlight)) + (concat + sep + + ;; eol type + (let ((eol (coding-system-eol-type buffer-file-coding-system))) + (when (or (eq doom-modeline-buffer-encoding t) + (and (eq doom-modeline-buffer-encoding 'nondefault) + (not (equal eol doom-modeline-default-eol-type)))) + (propertize + (pcase eol + (0 "LF ") + (1 "CRLF ") + (2 "CR ") + (_ "")) + 'face face + 'mouse-face mouse-face + 'help-echo (format "End-of-line style: %s\nmouse-1: Cycle" + (pcase eol + (0 "Unix-style LF") + (1 "DOS-style CRLF") + (2 "Mac-style CR") + (_ "Undecided"))) + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] 'mode-line-change-eol) + map)))) + + ;; coding system + (let* ((sys (coding-system-plist buffer-file-coding-system)) + (cat (plist-get sys :category)) + (sym (if (memq cat + '(coding-category-undecided coding-category-utf-8)) + 'utf-8 + (plist-get sys :name)))) + (when (or (eq doom-modeline-buffer-encoding t) + (and (eq doom-modeline-buffer-encoding 'nondefault) + (not (eq cat 'coding-category-undecided)) + (not (eq sym doom-modeline-default-coding-system)))) + (propertize + (upcase (symbol-name sym)) + 'face face + 'mouse-face mouse-face + 'help-echo 'mode-line-mule-info-help-echo + 'local-map mode-line-coding-system-map))) + + sep)))) + + +;; +;; Indentation +;; + +(doom-modeline-def-segment indent-info + "Displays the indentation information." + (when doom-modeline-indent-info + (let ((do-propertize + (lambda (mode size) + (propertize + (format " %s %d " mode size) + 'face (doom-modeline-face))))) + (if indent-tabs-mode + (funcall do-propertize "TAB" tab-width) + (let ((lookup-var + (seq-find (lambda (var) + (and var (boundp var) (symbol-value var))) + (cdr (assoc major-mode doom-modeline-indent-alist)) nil))) + (funcall do-propertize "SPC" + (if lookup-var + (symbol-value lookup-var) + tab-width))))))) + +;; +;; Remote host +;; + +(doom-modeline-def-segment remote-host + "Hostname for remote buffers." + (when default-directory + (when-let ((host (file-remote-p default-directory 'host))) + (propertize + (concat "@" host) + 'face (doom-modeline-face 'doom-modeline-host))))) + + +;; +;; Major mode +;; + +(doom-modeline-def-segment major-mode + "The major mode, including environment and text-scale info." + (let ((sep (doom-modeline-spc)) + (face (doom-modeline-face 'doom-modeline-buffer-major-mode))) + (concat + sep + (propertize (concat + (format-mode-line + (or (and (boundp 'delighted-modes) + (cadr (assq major-mode delighted-modes))) + mode-name)) + (when (and doom-modeline-env-version doom-modeline-env--version) + (format " %s" doom-modeline-env--version))) + 'help-echo "Major mode\n\ +mouse-1: Display major mode menu\n\ +mouse-2: Show help for major mode\n\ +mouse-3: Toggle minor modes" + 'face face + 'mouse-face 'doom-modeline-highlight + 'local-map mode-line-major-mode-keymap) + (and (boundp 'text-scale-mode-amount) + (/= text-scale-mode-amount 0) + (propertize + (format + (if (> text-scale-mode-amount 0) " (%+d)" " (%-d)") + text-scale-mode-amount) + 'face face)) + sep))) + + +;; +;; Process +;; + +(doom-modeline-def-segment process + "The process info." + (doom-modeline-display-text + (format-mode-line mode-line-process))) + + +;; +;; Minor modes +;; + +(doom-modeline-def-segment minor-modes + (when doom-modeline-minor-modes + (let ((sep (doom-modeline-spc)) + (face (doom-modeline-face 'doom-modeline-buffer-minor-mode)) + (mouse-face 'doom-modeline-highlight) + (help-echo "Minor mode + mouse-1: Display minor mode menu + mouse-2: Show help for minor mode + mouse-3: Toggle minor modes")) + (if (bound-and-true-p minions-mode) + `((:propertize ("" ,(minions--prominent-modes)) + face ,face + mouse-face ,mouse-face + help-echo ,help-echo + local-map ,mode-line-minor-mode-keymap) + ,sep + (:propertize ("" ,(doom-modeline-icon 'octicon "nf-oct-gear" "⚙" + minions-mode-line-lighter + :face face)) + mouse-face ,mouse-face + help-echo "Minions +mouse-1: Display minor modes menu" + local-map ,minions-mode-line-minor-modes-map) + ,sep) + `((:propertize ("" minor-mode-alist) + face ,face + mouse-face ,mouse-face + help-echo ,help-echo + local-map ,mode-line-minor-mode-keymap) + ,sep))))) + + +;; +;; VCS +;; + +(defun doom-modeline-vcs-icon (icon &optional unicode text face) + "Displays the vcs ICON with FACE and VOFFSET. + +UNICODE and TEXT are fallbacks. +Uses `nerd-icons-octicon' to fetch the icon." + (doom-modeline-icon 'devicon (and doom-modeline-vcs-icon icon) + unicode text :face face)) + +(defvar-local doom-modeline--vcs nil) +(defun doom-modeline-update-vcs (&rest _) + "Update vcs state in mode-line." + (setq doom-modeline--vcs + (when (and vc-mode buffer-file-name) + (let* ((backend (vc-backend buffer-file-name)) + (state (vc-state buffer-file-name backend)) + (icon (cond ((memq state '(edited added)) + (doom-modeline-vcs-icon "nf-dev-git_compare" "🔃" "*" 'doom-modeline-info)) + ((eq state 'needs-merge) + (doom-modeline-vcs-icon "nf-dev-git_merge" "🔀" "?" 'doom-modeline-info)) + ((eq state 'needs-update) + (doom-modeline-vcs-icon "nf-dev-git_pull_request" "⬇" "!" 'doom-modeline-warning)) + ((memq state '(removed conflict unregistered)) + (doom-modeline-icon 'octicon "nf-oct-alert" "⚠" "!" :face 'doom-modeline-urgent)) + (t (doom-modeline-vcs-icon "nf-dev-git_branch" "" "@" 'doom-modeline-info)))) + (str (if vc-display-status + (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2)) + "")) + (face (cond ((eq state 'needs-update) + '(doom-modeline-warning bold)) + ((memq state '(removed conflict unregistered)) + '(doom-modeline-urgent bold)) + (t '(doom-modeline-info bold)))) + (text (propertize (if (length> str doom-modeline-vcs-max-length) + (concat + (substring str 0 (- doom-modeline-vcs-max-length 3)) + doom-modeline-ellipsis) + str) + 'face face))) + `((icon . ,icon) (text . ,text)))))) +(add-hook 'find-file-hook #'doom-modeline-update-vcs) +(add-hook 'after-save-hook #'doom-modeline-update-vcs) +(advice-add #'vc-refresh-state :after #'doom-modeline-update-vcs) + +(doom-modeline-add-variable-watcher + 'doom-modeline-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-update-vcs)))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-unicode-fallback + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-unicode-fallback val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-update-vcs)))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-vcs-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-vcs-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-update-vcs)))))) + +(doom-modeline-def-segment vcs + "Displays the current branch, colored based on its state." + (when doom-modeline--vcs + (let-alist doom-modeline--vcs + (let ((sep (doom-modeline-spc)) + (vsep (doom-modeline-vspc))) + (concat sep + (propertize (concat + (doom-modeline-display-icon .icon) + vsep + (doom-modeline-display-text .text)) + 'help-echo (get-text-property 1 'help-echo vc-mode) + 'mouse-face 'doom-modeline-highlight + 'local-map (get-text-property 1 'local-map vc-mode)) + sep))))) + + +;; +;; Check +;; + +(defun doom-modeline-check-icon (icon unicode text face) + "Displays the check ICON with FACE. + +UNICODE and TEXT are fallbacks. +Uses `nerd-icons-mdicon' to fetch the icon." + (doom-modeline-icon 'mdicon (and doom-modeline-check-icon icon) + unicode text :face face)) + +(defun doom-modeline-check-text (text &optional face) + "Displays the check TEXT with FACE." + (propertize text 'face (or face 'mode-line))) + +;; Flycheck + +(defun doom-modeline--flycheck-count-errors () + "Count the number of ERRORS, grouped by level. + +Return an alist, where each ITEM is a cons cell whose `car' is an +error level, and whose `cdr' is the number of errors of that +level." + (let ((info 0) (warning 0) (error 0)) + (mapc + (lambda (item) + (let ((count (cdr item))) + (pcase (flycheck-error-level-compilation-level (car item)) + (0 (cl-incf info count)) + (1 (cl-incf warning count)) + (2 (cl-incf error count))))) + (flycheck-count-errors flycheck-current-errors)) + `((info . ,info) (warning . ,warning) (error . ,error)))) + +(defvar-local doom-modeline--flycheck nil) +(defun doom-modeline-update-flycheck (&optional status) + "Update flycheck via STATUS." + (setq doom-modeline--flycheck + (let-alist (doom-modeline--flycheck-count-errors) + (let* ((vsep (doom-modeline-vspc)) + (seg (if doom-modeline-check-simple-format + (let ((count (+ .error .warning .info))) + (pcase status + ('finished (if (> count 0) + (let ((face (if (> .error 0) 'doom-modeline-urgent 'doom-modeline-warning))) + (concat + (doom-modeline-check-icon "nf-md-alert_circle_outline" "⚠" "!" face) + vsep + (doom-modeline-check-text (number-to-string count) face))) + (doom-modeline-check-icon "nf-md-check_circle_outline" "✔" "" 'doom-modeline-info))) + ('running (concat + (doom-modeline-check-icon "nf-md-timer_sand" "⏳" "*" 'doom-modeline-debug) + (when (> count 0) + (concat + vsep + (doom-modeline-check-text (number-to-string count) 'doom-modeline-debug))))) + ('no-checker (doom-modeline-check-icon "nf-md-alert_box_outline" "⚠" "-" 'doom-modeline-debug)) + ('errored (doom-modeline-check-icon "nf-md-alert_box_outline" "⚠" "!" 'doom-modeline-urgent)) + ('interrupted (doom-modeline-check-icon "nf-md-pause_circle_outline" "⦷" "." 'doom-modeline-debug)) + ('suspicious (doom-modeline-check-icon "nf-md-file_question_outline" "❓" "?" 'doom-modeline-debug)) + (_ ""))) + (concat (doom-modeline-check-icon "nf-md-close_circle_outline" "⮾" "!" 'doom-modeline-urgent) + vsep + (doom-modeline-check-text (number-to-string .error) 'doom-modeline-urgent) + vsep + (doom-modeline-check-icon "nf-md-alert_outline" "⚠" "!" 'doom-modeline-warning) + vsep + (doom-modeline-check-text (number-to-string .warning) 'doom-modeline-warning) + vsep + (doom-modeline-check-icon "nf-md-information_outline" "🛈" "!" 'doom-modeline-info) + vsep + (doom-modeline-check-text (number-to-string .info) 'doom-modeline-info))))) + (propertize seg + 'help-echo (concat "Flycheck\n" + (pcase status + ('finished (format "error: %d, warning: %d, info: %d" .error .warning .info)) + ('running "Checking...") + ('no-checker "No Checker") + ('errored "Error") + ('interrupted "Interrupted") + ('suspicious "Suspicious")) + "\nmouse-1: Display minor mode menu\nmouse-2: Show help for minor mode") + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line down-mouse-1] + flycheck-mode-menu-map) + (define-key map [mode-line mouse-2] + (lambda () + (interactive) + (describe-function 'flycheck-mode))) + map)))))) +(add-hook 'flycheck-status-changed-functions #'doom-modeline-update-flycheck) +(add-hook 'flycheck-mode-hook #'doom-modeline-update-flycheck) + +(doom-modeline-add-variable-watcher + 'doom-modeline-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (bound-and-true-p flycheck-mode) + (doom-modeline-update-flycheck))))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-check-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-check-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (bound-and-true-p flycheck-mode) + (doom-modeline-update-flycheck))))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-unicode-fallback + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-unicode-fallback val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (bound-and-true-p flycheck-mode) + (doom-modeline-update-flycheck))))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-check-simple-format + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-check-simple-format val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (bound-and-true-p flycheck-mode) + (doom-modeline-update-flycheck))))))) + +;; Flymake + +;; Compatibility +;; @see https://github.com/emacs-mirror/emacs/commit/6e100869012da9244679696634cab6b9cac96303. +(with-eval-after-load 'flymake + (unless (boundp 'flymake--state) + (defvaralias 'flymake--state 'flymake--backend-state)) + (unless (fboundp 'flymake--state-diags) + (defalias 'flymake--state-diags 'flymake--backend-state-diags))) + +(defun doom-modeline--flymake-count-errors () + "Count the number of ERRORS, grouped by level." + (let ((warning-level (warning-numeric-level :warning)) + (note-level (warning-numeric-level :debug)) + (note 0) (warning 0) (error 0)) + (maphash (lambda (_b state) + (cl-loop + with diags = (flymake--state-diags state) + for diag in diags do + (let ((severity (flymake--lookup-type-property (flymake--diag-type diag) 'severity + (warning-numeric-level :error)))) + (cond ((> severity warning-level) (cl-incf error)) + ((> severity note-level) (cl-incf warning)) + (t (cl-incf note)))))) + flymake--state) + `((note . ,note) (warning . ,warning) (error . ,error)))) + +(defvar-local doom-modeline--flymake nil) +(defun doom-modeline-update-flymake (&rest _) + "Update flymake." + (setq doom-modeline--flymake + (let* ((known (hash-table-keys flymake--state)) + (running (flymake-running-backends)) + (disabled (flymake-disabled-backends)) + (reported (flymake-reporting-backends)) + (all-disabled (and disabled (null running))) + (some-waiting (cl-set-difference running reported))) + (let-alist (doom-modeline--flymake-count-errors) + (let* ((vsep (doom-modeline-vspc)) + (seg (if doom-modeline-check-simple-format + (let ((count (+ .error .warning .note))) + (cond + (some-waiting (concat + (doom-modeline-check-icon "nf-md-timer_sand" "⏳" "*" 'doom-modeline-debug) + (when (> count 0) + (concat + vsep + (doom-modeline-check-text (number-to-string count) 'doom-modeline-debug))))) + ((null known) (doom-modeline-check-icon "nf-md-alert_box_outline" "⚠" "!" 'doom-modeline-urgent)) + (all-disabled (doom-modeline-check-icon "nf-md-alert_box_outline" "⚠" "!" 'doom-modeline-warning)) + (t (if (> count 0) + (let ((face (if (> .error 0) 'doom-modeline-urgent 'doom-modeline-warning))) + (concat + (doom-modeline-check-icon "nf-md-alert_circle_outline" "⚠" "!" face) + vsep + (doom-modeline-check-text (number-to-string count) face))) + (doom-modeline-check-icon "nf-md-check_circle_outline" "✔" "" 'doom-modeline-info))))) + (concat (doom-modeline-check-icon "nf-md-close_circle_outline" "⮾" "!" 'doom-modeline-urgent) + vsep + (doom-modeline-check-text (number-to-string .error) 'doom-modeline-urgent) + vsep + (doom-modeline-check-icon "nf-md-alert_outline" "⚠" "!" 'doom-modeline-warning) + vsep + (doom-modeline-check-text (number-to-string .warning) 'doom-modeline-warning) + vsep + (doom-modeline-check-icon "nf-md-information_outline" "🛈" "!" 'doom-modeline-info) + vsep + (doom-modeline-check-text (number-to-string .note) 'doom-modeline-info))))) + (propertize + seg + 'help-echo (concat "Flymake\n" + (cond + (some-waiting "Checking...") + ((null known) "No Checker") + (all-disabled "All Checkers Disabled") + (t (format "%d/%d backends running\nerror: %d, warning: %d, note: %d" + (length running) (length known) .error .warning .note))) + "\nmouse-1: Display minor mode menu\nmouse-2: Show help for minor mode") + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line down-mouse-1] + flymake-menu) + (define-key map [mode-line mouse-2] + (lambda () + (interactive) + (describe-function 'flymake-mode))) + map))))))) +(advice-add #'flymake--handle-report :after #'doom-modeline-update-flymake) + +(doom-modeline-add-variable-watcher + 'doom-modeline-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (bound-and-true-p flymake-mode) + (doom-modeline-update-flymake))))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-check-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-check-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (bound-and-true-p flymake-mode) + (doom-modeline-update-flymake))))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-unicode-fallback + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-unicode-fallback val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (bound-and-true-p flymake-mode) + (doom-modeline-update-flymake))))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-check-simple-format + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-check-simple-format val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (bound-and-true-p flymake-mode) + (doom-modeline-update-flymake))))))) + +(doom-modeline-def-segment check + "Displays color-coded error status in the current buffer with pretty icons." + (when-let ((sep (doom-modeline-spc)) + (vsep (doom-modeline-vspc)) + (seg (cond + ((and (bound-and-true-p flymake-mode) + (bound-and-true-p flymake--state)) ; only support 26+ + doom-modeline--flymake) + ((and (bound-and-true-p flycheck-mode) + (bound-and-true-p flycheck--automatically-enabled-checkers)) + doom-modeline--flycheck)))) + (concat + sep + (let ((str)) + (dolist (s (split-string seg " ")) + (setq str + (concat str + (if (string-match-p "^[0-9]+$" s) + (concat vsep + (doom-modeline-display-text s) + vsep) + (doom-modeline-display-icon s))))) + (propertize str + 'help-echo (get-text-property 1 'help-echo seg) + 'mouse-face 'doom-modeline-highlight + 'local-map (get-text-property 1 'local-map seg))) + sep))) + + +;; +;; Word Count +;; + +(doom-modeline-def-segment word-count + "The buffer word count. +Displayed when in a major mode in `doom-modeline-continuous-word-count-modes'. +Respects `doom-modeline-enable-word-count'." + (when (and doom-modeline-enable-word-count + (member major-mode doom-modeline-continuous-word-count-modes)) + (propertize (format " %dW" (count-words (point-min) (point-max))) + 'face (doom-modeline-face)))) + + +;; +;; Selection +;; + +(defsubst doom-modeline-column (pos) + "Get the column of the position `POS'." + (save-excursion (goto-char pos) + (current-column))) + +(doom-modeline-def-segment selection-info + "Information about the current selection. + +Such as how many characters and lines are selected, or the NxM dimensions of a +block selection." + (when (and (or mark-active (and (bound-and-true-p evil-local-mode) + (eq evil-state 'visual))) + (doom-modeline--active)) + (cl-destructuring-bind (beg . end) + (if (and (bound-and-true-p evil-local-mode) (eq evil-state 'visual)) + (cons evil-visual-beginning evil-visual-end) + (cons (region-beginning) (region-end))) + (propertize + (let ((lines (count-lines beg (min end (point-max))))) + (concat + " " + (cond ((or (bound-and-true-p rectangle-mark-mode) + (and (bound-and-true-p evil-visual-selection) + (eq 'block evil-visual-selection))) + (let ((cols (abs (- (doom-modeline-column end) + (doom-modeline-column beg))))) + (format "%dx%dB" lines cols))) + ((and (bound-and-true-p evil-visual-selection) + (eq evil-visual-selection 'line)) + (format "%dL" lines)) + ((> lines 1) + (format "%dC %dL" (- end beg) lines)) + (t + (format "%dC" (- end beg)))) + (when doom-modeline-enable-word-count + (format " %dW" (count-words beg end))) + " ")) + 'face 'doom-modeline-emphasis)))) + + +;; +;; Matches (macro, anzu, evil-substitute, iedit, symbol-overlay and multi-cursors) +;; + +(defsubst doom-modeline--macro-recording () + "Display current Emacs or evil macro being recorded." + (when (and (doom-modeline--active) + (or defining-kbd-macro executing-kbd-macro)) + (let ((sep (propertize " " 'face 'doom-modeline-panel)) + (vsep (propertize " " 'face + '(:inherit (doom-modeline-panel variable-pitch)))) + (macro-name (if (bound-and-true-p evil-this-macro) + (format " @%s " + (char-to-string evil-this-macro)) + "Macro"))) + (concat + sep + (if doom-modeline-always-show-macro-register + (propertize macro-name 'face 'doom-modeline-panel) + (concat + (doom-modeline-icon 'mdicon "nf-md-record" "●" + macro-name + :face '(:inherit (doom-modeline-urgent doom-modeline-panel)) + :v-adjust 0.15) + vsep + (doom-modeline-icon 'mdicon "nf-md-menu_right" "▶" ">" + :face 'doom-modeline-panel + :v-adjust 0.15))) + sep)))) + +;; `anzu' and `evil-anzu' expose current/total state that can be displayed in the +;; mode-line. +(defun doom-modeline-fix-anzu-count (positions here) + "Calulate anzu count via POSITIONS and HERE." + (cl-loop for (start . end) in positions + collect t into before + when (and (>= here start) (<= here end)) + return (length before) + finally return 0)) + +(advice-add #'anzu--where-is-here :override #'doom-modeline-fix-anzu-count) + +(setq anzu-cons-mode-line-p nil) ; manage modeline segment ourselves +;; Ensure anzu state is cleared when searches & iedit are done +(with-eval-after-load 'anzu + (add-hook 'isearch-mode-end-hook #'anzu--reset-status t) + (add-hook 'iedit-mode-end-hook #'anzu--reset-status) + (advice-add #'evil-force-normal-state :after #'anzu--reset-status) + ;; Fix matches segment mirroring across all buffers + (mapc #'make-variable-buffer-local + '(anzu--total-matched + anzu--current-position anzu--state anzu--cached-count + anzu--cached-positions anzu--last-command + anzu--last-isearch-string anzu--overflow-p))) + +(defsubst doom-modeline--anzu () + "Show the match index and total number thereof. +Requires `anzu', also `evil-anzu' if using `evil-mode' for compatibility with +`evil-search'." + (when (and (bound-and-true-p anzu--state) + (not (bound-and-true-p iedit-mode))) + (propertize + (let ((here anzu--current-position) + (total anzu--total-matched)) + (cond ((eq anzu--state 'replace-query) + (format " %d replace " anzu--cached-count)) + ((eq anzu--state 'replace) + (format " %d/%d " here total)) + (anzu--overflow-p + (format " %s+ " total)) + (t + (format " %s/%d " here total)))) + 'face (doom-modeline-face 'doom-modeline-panel)))) + +(defsubst doom-modeline--evil-substitute () + "Show number of matches for `evil-ex' in real time. +The number of matches contains substitutions and highlightings." + (when (and (bound-and-true-p evil-local-mode) + (or (assq 'evil-ex-substitute evil-ex-active-highlights-alist) + (assq 'evil-ex-global-match evil-ex-active-highlights-alist) + (assq 'evil-ex-buffer-match evil-ex-active-highlights-alist))) + (propertize + (let ((range (if evil-ex-range + (cons (car evil-ex-range) (cadr evil-ex-range)) + (cons (line-beginning-position) (line-end-position)))) + (pattern (car-safe (evil-delimited-arguments evil-ex-argument 2)))) + (if pattern + (format " %s matches " (how-many pattern (car range) (cdr range))) + " - ")) + 'face (doom-modeline-face 'doom-modeline-panel)))) + +(defun doom-modeline-themes--overlay-sort (a b) + "Sort overlay A and B." + (< (overlay-start a) (overlay-start b))) + +(defsubst doom-modeline--iedit () + "Show the number of iedit regions matches + what match you're on." + (when (and (bound-and-true-p iedit-mode) + (bound-and-true-p iedit-occurrences-overlays)) + (propertize + (let ((this-oc (or (let ((inhibit-message t)) + (iedit-find-current-occurrence-overlay)) + (save-excursion (iedit-prev-occurrence) + (iedit-find-current-occurrence-overlay)))) + (length (length iedit-occurrences-overlays))) + (format " %s/%d " + (if this-oc + (- length + (length (memq this-oc (sort (append iedit-occurrences-overlays nil) + #'doom-modeline-themes--overlay-sort))) + -1) + "-") + length)) + 'face (doom-modeline-face 'doom-modeline-panel)))) + +(defsubst doom-modeline--symbol-overlay () + "Show the number of matches for symbol overlay." + (when (and (doom-modeline--active) + (bound-and-true-p symbol-overlay-keywords-alist) + (not (bound-and-true-p symbol-overlay-temp-symbol)) + (not (bound-and-true-p iedit-mode))) + (let* ((keyword (symbol-overlay-assoc (symbol-overlay-get-symbol t))) + (symbol (car keyword)) + (before (symbol-overlay-get-list -1 symbol)) + (after (symbol-overlay-get-list 1 symbol)) + (count (length before))) + (if (symbol-overlay-assoc symbol) + (propertize + (format (concat " %d/%d " (and (cadr keyword) "in scope ")) + (+ count 1) + (+ count (length after))) + 'face (doom-modeline-face 'doom-modeline-panel)))))) + +(defsubst doom-modeline--multiple-cursors () + "Show the number of multiple cursors." + (cl-destructuring-bind (count . face) + (cond ((bound-and-true-p multiple-cursors-mode) + (cons (mc/num-cursors) + (doom-modeline-face 'doom-modeline-panel))) + ((bound-and-true-p evil-mc-cursor-list) + (cons (length evil-mc-cursor-list) + (doom-modeline-face (if evil-mc-frozen + 'doom-modeline-bar + 'doom-modeline-panel)))) + ((cons nil nil))) + (when count + (concat (propertize " " 'face face) + (if (doom-modeline-icon-displayable-p) + (doom-modeline-icon 'faicon "nf-fa-i_cursor" "" "" :face face) + (propertize "I" + 'face `(:inherit ,face :height 1.4 :weight normal) + 'display '(raise -0.1))) + (propertize " " + 'face `(:inherit (variable-pitch ,face))) + (propertize (format "%d " count) + 'face face))))) + +(defsubst doom-modeline--phi-search () + "Show the number of matches for `phi-search' and `phi-replace'." + (when (and (doom-modeline--active) + (bound-and-true-p phi-search--overlays)) + (let ((total (length phi-search--overlays)) + (selection phi-search--selection)) + (when selection + (propertize + (format " %d/%d " (1+ selection) total) + 'face (doom-modeline-face 'doom-modeline-panel)))))) + +(defun doom-modeline--override-phi-search (orig-fun &rest args) + "Override the mode-line of `phi-search' and `phi-replace'. +Apply ORIG-FUN with ARGS." + (if (bound-and-true-p doom-modeline-mode) + (apply orig-fun mode-line-format (cdr args)) + (apply orig-fun args))) +(advice-add #'phi-search--initialize :around #'doom-modeline--override-phi-search) + +(defsubst doom-modeline--buffer-size () + "Show buffer size." + (when size-indication-mode + (let ((sep (doom-modeline-spc))) + (concat sep + (propertize "%I" + 'face (doom-modeline-face) + 'help-echo "Buffer size +mouse-1: Display Line and Column Mode Menu" + 'mouse-face 'doom-modeline-highlight + 'local-map mode-line-column-line-number-mode-map) + sep)))) + +(doom-modeline-def-segment matches + "Displays matches. + +Including: +1. the currently recording macro, 2. A current/total for the +current search term (with `anzu'), 3. The number of substitutions being +conducted with `evil-ex-substitute', and/or 4. The number of active `iedit' +regions, 5. The current/total for the highlight term (with `symbol-overlay'), +6. The number of active `multiple-cursors'." + (let ((meta (concat (doom-modeline--macro-recording) + (doom-modeline--anzu) + (doom-modeline--phi-search) + (doom-modeline--evil-substitute) + (doom-modeline--iedit) + (doom-modeline--symbol-overlay) + (doom-modeline--multiple-cursors)))) + (or (and (not (string-empty-p meta)) meta) + (doom-modeline--buffer-size)))) + +(doom-modeline-def-segment buffer-size + "Display buffer size." + (doom-modeline--buffer-size)) + +;; +;; Media +;; + +(doom-modeline-def-segment media-info + "Metadata regarding the current file, such as dimensions for images." + ;; TODO: Include other information + (cond ((eq major-mode 'image-mode) + (cl-destructuring-bind (width . height) + (when (fboundp 'image-size) + (image-size (image-get-display-property) :pixels)) + (format " %dx%d " width height))))) + + +;; +;; Bars +;; + +(defvar doom-modeline--bar-active nil) +(defvar doom-modeline--bar-inactive nil) + +(defsubst doom-modeline--bar () + "The default bar regulates the height of the mode-line in GUI." + (unless (and doom-modeline--bar-active doom-modeline--bar-inactive) + (let ((width doom-modeline-bar-width) + (height (max doom-modeline-height (doom-modeline--font-height)))) + (setq doom-modeline--bar-active + (doom-modeline--create-bar-image 'doom-modeline-bar width height) + doom-modeline--bar-inactive + (doom-modeline--create-bar-image + 'doom-modeline-bar-inactive width height)))) + (if (doom-modeline--active) + doom-modeline--bar-active + doom-modeline--bar-inactive)) + +(defun doom-modeline-refresh-bars () + "Refresh mode-line bars on next redraw." + (setq doom-modeline--bar-active nil + doom-modeline--bar-inactive nil)) + +(cl-defstruct doom-modeline--hud-cache active inactive top-margin bottom-margin) + +(defsubst doom-modeline--hud () + "Powerline's hud segment reimplemented in the style of Doom's bar segment." + (let* ((ws (window-start)) + (we (window-end)) + (bs (buffer-size)) + (height (max doom-modeline-height (doom-modeline--font-height))) + (top-margin (if (zerop bs) + 0 + (/ (* height (1- ws)) bs))) + (bottom-margin (if (zerop bs) + 0 + (max 0 (/ (* height (- bs we 1)) bs)))) + (cache (or (window-parameter nil 'doom-modeline--hud-cache) + (set-window-parameter + nil + 'doom-modeline--hud-cache + (make-doom-modeline--hud-cache))))) + (unless (and (doom-modeline--hud-cache-active cache) + (doom-modeline--hud-cache-inactive cache) + (= top-margin (doom-modeline--hud-cache-top-margin cache)) + (= bottom-margin + (doom-modeline--hud-cache-bottom-margin cache))) + (setf (doom-modeline--hud-cache-active cache) + (doom-modeline--create-hud-image + 'doom-modeline-bar 'default doom-modeline-bar-width + height top-margin bottom-margin) + (doom-modeline--hud-cache-inactive cache) + (doom-modeline--create-hud-image + 'doom-modeline-bar-inactive 'default doom-modeline-bar-width + height top-margin bottom-margin) + (doom-modeline--hud-cache-top-margin cache) top-margin + (doom-modeline--hud-cache-bottom-margin cache) bottom-margin)) + (if (doom-modeline--active) + (doom-modeline--hud-cache-active cache) + (doom-modeline--hud-cache-inactive cache)))) + +(defun doom-modeline-invalidate-huds () + "Invalidate all cached hud images." + (dolist (frame (frame-list)) + (dolist (window (window-list frame)) + (set-window-parameter window 'doom-modeline--hud-cache nil)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-height + (lambda (_sym val op _where) + (when (and (eq op 'set) (integerp val)) + (doom-modeline-refresh-bars) + (doom-modeline-invalidate-huds)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-bar-width + (lambda (_sym val op _where) + (when (and (eq op 'set) (integerp val)) + (doom-modeline-refresh-bars) + (doom-modeline-invalidate-huds)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-icon + (lambda (_sym _val op _where) + (when (eq op 'set) + (doom-modeline-refresh-bars) + (doom-modeline-invalidate-huds)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-unicode-fallback + (lambda (_sym _val op _where) + (when (eq op 'set) + (doom-modeline-refresh-bars) + (doom-modeline-invalidate-huds)))) + +(add-hook 'window-configuration-change-hook #'doom-modeline-refresh-bars) +(add-hook 'window-configuration-change-hook #'doom-modeline-invalidate-huds) + +(doom-modeline-def-segment bar + "The bar regulates the height of the `doom-modeline' in GUI." + (when (display-graphic-p) + (concat + (if doom-modeline-hud + (doom-modeline--hud) + (doom-modeline--bar)) + (doom-modeline-spc)))) + +(doom-modeline-def-segment hud + "Powerline's hud segment reimplemented in the style of bar segment." + (when (display-graphic-p) + (concat + (doom-modeline--hud) + (doom-modeline-spc)))) + + +;; +;; Window number +;; + +;; HACK: `ace-window-display-mode' should respect the ignore buffers. +(defun doom-modeline-aw-update () + "Update ace-window-path window parameter for all windows. +Ensure all windows are labeled so the user can select a specific +one. The ignored buffers are excluded unless `aw-ignore-on' is nil." + (let ((ignore-window-parameters t)) + (avy-traverse + (avy-tree (aw-window-list) aw-keys) + (lambda (path leaf) + (set-window-parameter + leaf 'ace-window-path + (propertize + (apply #'string (reverse path)) + 'face 'aw-mode-line-face)))))) +(advice-add #'aw-update :override #'doom-modeline-aw-update) + +;; Remove original window number of `ace-window-display-mode'. +(add-hook 'ace-window-display-mode-hook + (lambda () + (setq-default mode-line-format + (assq-delete-all 'ace-window-display-mode + (default-value 'mode-line-format))))) + +(advice-add #'window-numbering-install-mode-line :override #'ignore) +(advice-add #'window-numbering-clear-mode-line :override #'ignore) +(advice-add #'winum--install-mode-line :override #'ignore) +(advice-add #'winum--clear-mode-line :override #'ignore) + +(doom-modeline-def-segment window-number + "The current window number." + (let ((num (cond + ((bound-and-true-p ace-window-display-mode) + (aw-update) + (window-parameter (selected-window) 'ace-window-path)) + ((bound-and-true-p winum-mode) + (setq winum-auto-setup-mode-line nil) + (winum-get-number-string)) + ((bound-and-true-p window-numbering-mode) + (window-numbering-get-number-string)) + (t "")))) + (when (and (length> num 0) + (length> (cl-mapcan + (lambda (frame) + ;; Exclude minibuffer, tooltip and child frames + (unless (or (and (fboundp 'frame-parent) (frame-parent frame)) + (string= (frame-parameter frame 'name) + (alist-get 'name (bound-and-true-p tooltip-frame-parameters)))) + (window-list frame 'never))) + (visible-frame-list)) + 1)) + (propertize (format " %s " num) + 'face (doom-modeline-face 'doom-modeline-buffer-major-mode))))) + + +;; +;; Workspace +;; + +(doom-modeline-def-segment workspace-name + "The current workspace name or number. +Requires `eyebrowse-mode' to be enabled or `tab-bar-mode' tabs to be created." + (when doom-modeline-workspace-name + (when-let + ((name (cond + ((and (bound-and-true-p eyebrowse-mode) + (length> (eyebrowse--get 'window-configs) 1)) + (setq mode-line-misc-info + (assq-delete-all 'eyebrowse-mode mode-line-misc-info)) + (when-let* + ((num (eyebrowse--get 'current-slot)) + (tag (nth 2 (assoc num (eyebrowse--get 'window-configs))))) + (if (length> tag 0) tag (int-to-string num)))) + ((and (fboundp 'tab-bar-mode) + (length> (frame-parameter nil 'tabs) 1)) + (let* ((current-tab (tab-bar--current-tab)) + (tab-index (tab-bar--current-tab-index)) + (explicit-name (alist-get 'explicit-name current-tab)) + (tab-name (alist-get 'name current-tab))) + (if explicit-name tab-name (+ 1 tab-index))))))) + (propertize (format " %s " name) + 'face (doom-modeline-face 'doom-modeline-buffer-major-mode))))) + + +;; +;; Perspective +;; + +(defvar-local doom-modeline--persp-name nil) +(defun doom-modeline-update-persp-name (&rest _) + "Update perspective name in mode-line." + (setq doom-modeline--persp-name + ;; Support `persp-mode', while not support `perspective' + (when (and doom-modeline-persp-name + (bound-and-true-p persp-mode) + (fboundp 'safe-persp-name) + (fboundp 'get-current-persp)) + (let* ((persp (get-current-persp)) + (name (safe-persp-name persp)) + (face (if (and persp + (not (persp-contain-buffer-p (current-buffer) persp))) + 'doom-modeline-persp-buffer-not-in-persp + 'doom-modeline-persp-name)) + (icon (doom-modeline-icon 'octicon "nf-oct-repo" "🖿" "#" + :face `(:inherit ,face :slant normal)))) + (when (or doom-modeline-display-default-persp-name + (not (string-equal persp-nil-name name))) + (concat " " + (propertize (concat (and doom-modeline-persp-icon + (concat icon + (propertize + " " + 'display '((space :relative-width 0.5))))) + (propertize name 'face face)) + 'help-echo "mouse-1: Switch perspective +mouse-2: Show help for minor mode" + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] + #'persp-switch) + (define-key map [mode-line mouse-2] + (lambda () + (interactive) + (describe-function 'persp-mode))) + map)) + " ")))))) + +(add-hook 'buffer-list-update-hook #'doom-modeline-update-persp-name) +(add-hook 'find-file-hook #'doom-modeline-update-persp-name) +(add-hook 'persp-activated-functions #'doom-modeline-update-persp-name) +(add-hook 'persp-renamed-functions #'doom-modeline-update-persp-name) +(advice-add #'lv-message :after #'doom-modeline-update-persp-name) + +(doom-modeline-def-segment persp-name + "The current perspective name." + (when (doom-modeline--segment-visible 'persp-name) + doom-modeline--persp-name)) + + +;; +;; Misc info +;; + +(doom-modeline-def-segment misc-info + "Mode line construct for miscellaneous information. +By default, this shows the information specified by `global-mode-string'." + (when (and (doom-modeline--segment-visible 'misc-info) + (or doom-modeline-display-misc-in-all-mode-lines + (doom-modeline--active))) + (doom-modeline-display-text + (format-mode-line mode-line-misc-info)))) + + +;; +;; Position +;; + +(doom-modeline-def-segment buffer-position + "The buffer position information." + (let ((visible (doom-modeline--segment-visible 'buffer-position)) + (sep (doom-modeline-spc)) + (wsep (doom-modeline-wspc)) + (face (doom-modeline-face)) + (help-echo "Buffer percentage\n\ +mouse-1: Display Line and Column Mode Menu") + (mouse-face 'doom-modeline-highlight) + (local-map mode-line-column-line-number-mode-map)) + `(,wsep + + ;; Line and column + (:propertize + ((line-number-mode + (column-number-mode + (doom-modeline-column-zero-based + doom-modeline-position-column-line-format + ,(string-replace + "%c" "%C" (car doom-modeline-position-column-line-format))) + doom-modeline-position-line-format) + (column-number-mode + (doom-modeline-column-zero-based + doom-modeline-position-column-format + ,(string-replace + "%c" "%C" (car doom-modeline-position-column-format))))) + (doom-modeline-total-line-number + ,(format "/%d" (line-number-at-pos (point-max))))) + face ,face + help-echo ,help-echo + mouse-face ,mouse-face + local-map ,local-map) + + ((or line-number-mode column-number-mode) + ,sep) + + ;; Position + (,visible + ,(cond + ((and (bound-and-true-p nyan-mode) + (>= (window-width) nyan-minimum-window-width)) + (concat sep (nyan-create) sep)) + ((and (bound-and-true-p poke-line-mode) + (>= (window-width) poke-line-minimum-window-width)) + (concat sep (poke-line-create) sep)) + ((and (bound-and-true-p mlscroll-mode) + (>= (window-width) mlscroll-minimum-current-width)) + (concat + sep + (let ((mlscroll-right-align nil)) + (format-mode-line (mlscroll-mode-line))) + sep)) + ((and (bound-and-true-p sml-modeline-mode) + (>= (window-width) sml-modeline-len)) + (concat sep (sml-modeline-create) sep)) + (t ""))) + + ;; Percent position + (doom-modeline-percent-position + ((:propertize ("" doom-modeline-percent-position) + face ,face + help-echo ,help-echo + mouse-face ,mouse-face + local-map ,local-map) + ,sep))))) + +;; +;; Party parrot +;; +(doom-modeline-def-segment parrot + "The party parrot animated icon. Requires `parrot-mode' to be enabled." + (when (and (doom-modeline--segment-visible 'parrot) + (bound-and-true-p parrot-mode)) + (concat (doom-modeline-wspc) + (parrot-create) + (doom-modeline-spc)))) + +;; +;; Modals (evil, overwrite, god, ryo and xah-fly-keys, etc.) +;; + +(defun doom-modeline--modal-icon (text face help-echo &optional icon unicode) + "Display the model icon with FACE and HELP-ECHO. +TEXT is alternative if icon is not available." + (propertize (doom-modeline-icon + 'mdicon + (and doom-modeline-modal-icon + (or (and doom-modeline-modal-modern-icon icon) + "nf-md-record")) + (or (and doom-modeline-modal-modern-icon unicode) "●") + text + :face (doom-modeline-face face)) + 'help-echo help-echo)) + +(defsubst doom-modeline--evil () + "The current evil state. Requires `evil-mode' to be enabled." + (when (bound-and-true-p evil-local-mode) + (doom-modeline--modal-icon + (let ((tag (evil-state-property evil-state :tag t))) + (if (stringp tag) tag (funcall tag))) + (cond + ((evil-normal-state-p) 'doom-modeline-evil-normal-state) + ((evil-emacs-state-p) 'doom-modeline-evil-emacs-state) + ((evil-insert-state-p) 'doom-modeline-evil-insert-state) + ((evil-motion-state-p) 'doom-modeline-evil-motion-state) + ((evil-visual-state-p) 'doom-modeline-evil-visual-state) + ((evil-operator-state-p) 'doom-modeline-evil-operator-state) + ((evil-replace-state-p) 'doom-modeline-evil-replace-state) + (t 'doom-modeline-evil-normal-state)) + (evil-state-property evil-state :name t) + (cond + ((evil-normal-state-p) "nf-md-alpha_n_circle") + ((evil-emacs-state-p) "nf-md-alpha_e_circle") + ((evil-insert-state-p) "nf-md-alpha_i_circle") + ((evil-motion-state-p) "nf-md-alpha_m_circle") + ((evil-visual-state-p) "nf-md-alpha_v_circle") + ((evil-operator-state-p) "nf-md-alpha_o_circle") + ((evil-replace-state-p) "nf-md-alpha_r_circle") + (t "nf-md-alpha_n_circle")) + (cond + ((evil-normal-state-p) "🅝") + ((evil-emacs-state-p) "🅔") + ((evil-insert-state-p) "🅘") + ((evil-motion-state-p) "🅜") + ((evil-visual-state-p) "🅥") + ((evil-operator-state-p) "🅞") + ((evil-replace-state-p) "🅡") + (t "🅝"))))) + +(defsubst doom-modeline--overwrite () + "The current overwrite state which is enabled by command `overwrite-mode'." + (when (and (bound-and-true-p overwrite-mode) + (not (bound-and-true-p evil-local-mode))) + (doom-modeline--modal-icon + "<W>" 'doom-modeline-overwrite "Overwrite mode" + "nf-md-marker" "🅦"))) + +(defsubst doom-modeline--god () + "The current god state which is enabled by the command `god-mode'." + (when (bound-and-true-p god-local-mode) + (doom-modeline--modal-icon + "<G>" 'doom-modeline-god "God mode" + "nf-md-account_circle" "🅖"))) + +(defsubst doom-modeline--ryo () + "The current ryo-modal state which is enabled by the command `ryo-modal-mode'." + (when (bound-and-true-p ryo-modal-mode) + (doom-modeline--modal-icon + "<R>" 'doom-modeline-ryo "Ryo modal" + "nf-md-star_circle" "✪"))) + +(defsubst doom-modeline--xah-fly-keys () + "The current `xah-fly-keys' state." + (when (bound-and-true-p xah-fly-keys) + (if xah-fly-insert-state-p + (doom-modeline--modal-icon + "<I>" 'doom-modeline-fly-insert-state "Xah-fly insert mode" + "nf-md-airplane_edit" "🛧") + (doom-modeline--modal-icon + "<C>" 'doom-modeline-fly-normal-state "Xah-fly command mode" + "nf-md-airplane_cog" "🛧")))) + +(defsubst doom-modeline--boon () + "The current Boon state. Requires `boon-mode' to be enabled." + (when (bound-and-true-p boon-local-mode) + (doom-modeline--modal-icon + (boon-state-string) + (cond + (boon-command-state 'doom-modeline-boon-command-state) + (boon-insert-state 'doom-modeline-boon-insert-state) + (boon-special-state 'doom-modeline-boon-special-state) + (boon-off-state 'doom-modeline-boon-off-state) + (t 'doom-modeline-boon-off-state)) + (boon-modeline-string) + "nf-md-coffee" "🍵"))) + +(defsubst doom-modeline--meow () + "The current Meow state. Requires `meow-mode' to be enabled." + (when (bound-and-true-p meow-mode) + (doom-modeline--modal-icon + (symbol-name (meow--current-state)) + (cond + ((meow-normal-mode-p) 'doom-modeline-evil-normal-state) + ((meow-insert-mode-p) 'doom-modeline-evil-insert-state) + ((meow-beacon-mode-p) 'doom-modeline-evil-visual-state) + ((meow-motion-mode-p) 'doom-modeline-evil-motion-state) + ((meow-keypad-mode-p) 'doom-modeline-evil-operator-state) + (t 'doom-modeline-evil-normal-state)) + (symbol-name (meow--current-state)) + (cond + ((meow-normal-mode-p) "nf-md-alpha_n_circle") + ((meow-insert-mode-p) "nf-md-alpha_i_circle") + ((meow-beacon-mode-p) "nf-md-alpha_b_circle") + ((meow-motion-mode-p) "nf-md-alpha_m_circle") + ((meow-keypad-mode-p) "nf-md-alpha_k_circle") + (t "nf-md-alpha_n_circle")) + (cond + ((meow-normal-mode-p) "🅝") + ((meow-insert-mode-p) "🅘") + ((meow-beacon-mode-p) "🅑") + ((meow-motion-mode-p) "🅜") + ((meow-keypad-mode-p) "🅚") + (t "🅝"))))) + +(doom-modeline-def-segment modals + "Displays modal editing states. + +Including `evil', `overwrite', `god', `ryo' and `xha-fly-kyes', etc." + (when doom-modeline-modal + (let* ((evil (doom-modeline--evil)) + (ow (doom-modeline--overwrite)) + (god (doom-modeline--god)) + (ryo (doom-modeline--ryo)) + (xf (doom-modeline--xah-fly-keys)) + (boon (doom-modeline--boon)) + (vsep (doom-modeline-vspc)) + (meow (doom-modeline--meow)) + (sep (and (or evil ow god ryo xf boon) (doom-modeline-spc)))) + (concat sep + (and evil (concat evil (and (or ow god ryo xf boon meow) vsep))) + (and ow (concat ow (and (or god ryo xf boon meow) vsep))) + (and god (concat god (and (or ryo xf boon meow) vsep))) + (and ryo (concat ryo (and (or xf boon meow) vsep))) + (and xf (concat xf (and (or boon meow) vsep))) + (and boon (concat boon (and meow vsep))) + meow + sep)))) + +;; +;; Objed state +;; + +(defvar doom-modeline--objed-active nil) + +(defun doom-modeline-update-objed (_ &optional reset) + "Update `objed' status, inactive when RESET is true." + (setq doom-modeline--objed-active (not reset))) + +(setq objed-modeline-setup-func #'doom-modeline-update-objed) + +(doom-modeline-def-segment objed-state () + "The current objed state." + (when (and doom-modeline--objed-active + (doom-modeline--active)) + (propertize (format " %s(%s) " + (symbol-name objed--object) + (char-to-string (aref (symbol-name objed--obj-state) 0))) + 'face 'doom-modeline-evil-emacs-state + 'help-echo (format "Objed object: %s (%s)" + (symbol-name objed--object) + (symbol-name objed--obj-state))))) + + +;; +;; Input method +;; + +(doom-modeline-def-segment input-method + "The current input method." + (when-let ((im (cond + (current-input-method + current-input-method-title) + ((and (bound-and-true-p evil-local-mode) + (bound-and-true-p evil-input-method)) + (nth 3 (assoc default-input-method input-method-alist))) + (t nil))) + (sep (doom-modeline-spc))) + (concat + sep + (propertize im + 'face (doom-modeline-face + (if (and (bound-and-true-p rime-mode) + (equal current-input-method "rime")) + (if (and (rime--should-enable-p) + (not (rime--should-inline-ascii-p))) + 'doom-modeline-input-method + 'doom-modeline-input-method-alt) + 'doom-modeline-input-method)) + 'help-echo (concat + "Current input method: " + current-input-method + "\n\ +mouse-2: Disable input method\n\ +mouse-3: Describe current input method") + 'mouse-face 'doom-modeline-highlight + 'local-map mode-line-input-method-map) + sep))) + + +;; +;; Info +;; + +(doom-modeline-def-segment info-nodes + "The topic and nodes in the Info buffer." + (concat + " (" + ;; topic + (propertize (if (stringp Info-current-file) + (replace-regexp-in-string + "%" "%%" + (file-name-sans-extension + (file-name-nondirectory Info-current-file))) + (format "*%S*" Info-current-file)) + 'face (doom-modeline-face 'doom-modeline-info)) + ") " + ;; node + (when Info-current-node + (propertize (replace-regexp-in-string + "%" "%%" Info-current-node) + 'face (doom-modeline-face 'doom-modeline-buffer-path) + 'help-echo + "mouse-1: scroll forward, mouse-3: scroll back" + 'mouse-face 'doom-modeline-highlight + 'local-map Info-mode-line-node-keymap)))) + + +;; +;; REPL +;; + +(defun doom-modeline-repl-icon (text face) + "Display REPL icon (or TEXT in terminal) with FACE." + (doom-modeline-icon 'faicon "nf-fa-terminal" "$" text :face face)) + +(defvar doom-modeline--cider nil) + +(defun doom-modeline-update-cider () + "Update cider repl state." + (setq doom-modeline--cider + (let* ((connected (cider-connected-p)) + (face (if connected 'doom-modeline-repl-success 'doom-modeline-repl-warning)) + (repl-buffer (cider-current-repl nil nil)) + (cider-info (when repl-buffer + (cider--connection-info repl-buffer t))) + (icon (doom-modeline-repl-icon "REPL" face))) + (propertize icon + 'help-echo + (if connected + (format "CIDER Connected %s\nmouse-2: CIDER quit" cider-info) + "CIDER Disconnected\nmouse-1: CIDER jack-in") + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (if connected + (define-key map [mode-line mouse-2] + #'cider-quit) + (define-key map [mode-line mouse-1] + #'cider-jack-in)) + map))))) + +(add-hook 'cider-connected-hook #'doom-modeline-update-cider) +(add-hook 'cider-disconnected-hook #'doom-modeline-update-cider) +(add-hook 'cider-mode-hook #'doom-modeline-update-cider) + +(doom-modeline-def-segment repl + "The REPL state." + (when doom-modeline-repl + (when-let ((icon (when (bound-and-true-p cider-mode) + doom-modeline--cider)) + (sep (doom-modeline-spc))) + (concat + sep + (doom-modeline-display-icon icon) + sep)))) + + +;; +;; LSP +;; + +(defun doom-modeline-lsp-icon (text face) + "Display LSP icon (or TEXT in terminal) with FACE." + (if doom-modeline-lsp-icon + (doom-modeline-icon 'octicon "nf-oct-rocket" "🚀" text :face face) + (propertize text 'face face))) + +(defvar-local doom-modeline--lsp nil) +(defun doom-modeline-update-lsp (&rest _) + "Update `lsp-mode' state." + (setq doom-modeline--lsp + (let* ((workspaces (lsp-workspaces)) + (face (if workspaces 'doom-modeline-lsp-success 'doom-modeline-lsp-warning)) + (icon (doom-modeline-lsp-icon "LSP" face))) + (propertize icon + 'help-echo + (if workspaces + (concat "LSP Connected " + (string-join + (mapcar (lambda (w) + (format "[%s]\n" (lsp--workspace-print w))) + workspaces)) + "C-mouse-1: Switch to another workspace folder +mouse-1: Describe current session +mouse-2: Quit server +mouse-3: Reconnect to server") + "LSP Disconnected +mouse-1: Reload to start server") + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (if workspaces + (progn + (define-key map [mode-line C-mouse-1] + #'lsp-workspace-folders-open) + (define-key map [mode-line mouse-1] + #'lsp-describe-session) + (define-key map [mode-line mouse-2] + #'lsp-workspace-shutdown) + (define-key map [mode-line mouse-3] + #'lsp-workspace-restart)) + (progn + (define-key map [mode-line mouse-1] + (lambda () + (interactive) + (ignore-errors (revert-buffer t t)))))) + map))))) +(add-hook 'lsp-before-initialize-hook #'doom-modeline-update-lsp) +(add-hook 'lsp-after-initialize-hook #'doom-modeline-update-lsp) +(add-hook 'lsp-after-uninitialized-functions #'doom-modeline-update-lsp) +(add-hook 'lsp-before-open-hook #'doom-modeline-update-lsp) +(add-hook 'lsp-after-open-hook #'doom-modeline-update-lsp) + +(defun doom-modeline--eglot-pending-count (server) + "Get count of pending eglot requests to SERVER." + (if (fboundp 'jsonrpc-continuation-count) + (jsonrpc-continuation-count server) + (hash-table-count (jsonrpc--request-continuations server)))) + +(defvar-local doom-modeline--eglot nil) +(defun doom-modeline-update-eglot () + "Update `eglot' state." + (setq doom-modeline--eglot + (pcase-let* ((server (and (eglot-managed-p) (eglot-current-server))) + (nick (and server (eglot--project-nickname server))) + (pending (and server (doom-modeline--eglot-pending-count server))) + (last-error (and server (jsonrpc-last-error server))) + (face (cond (last-error 'doom-modeline-lsp-error) + ((and pending (cl-plusp pending)) 'doom-modeline-lsp-warning) + (nick 'doom-modeline-lsp-success) + (t 'doom-modeline-lsp-warning))) + (icon (doom-modeline-lsp-icon "EGLOT" face))) + (propertize icon + 'help-echo (cond + (last-error + (format "EGLOT\nAn error occured: %s +mouse-3: Clear this status" (plist-get last-error :message))) + ((and pending (cl-plusp pending)) + (format "EGLOT\n%d outstanding requests" pending)) + (nick (format "EGLOT Connected (%s/%s) +C-mouse-1: Go to server errors +mouse-1: Go to server events +mouse-2: Quit server +mouse-3: Reconnect to server" nick (eglot--major-modes server))) + (t "EGLOT Disconnected +mouse-1: Start server")) + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (cond (last-error + (define-key map [mode-line mouse-3] + #'eglot-clear-status)) + ((and pending (cl-plusp pending)) + (define-key map [mode-line mouse-3] + #'eglot-forget-pending-continuations)) + (nick + (define-key map [mode-line C-mouse-1] + #'eglot-stderr-buffer) + (define-key map [mode-line mouse-1] + #'eglot-events-buffer) + (define-key map [mode-line mouse-2] + #'eglot-shutdown) + (define-key map [mode-line mouse-3] + #'eglot-reconnect)) + (t (define-key map [mode-line mouse-1] + #'eglot))) + map))))) +(add-hook 'eglot-managed-mode-hook #'doom-modeline-update-eglot) + +(defvar-local doom-modeline--tags nil) +(defun doom-modeline-update-tags () + "Update tags state." + (setq doom-modeline--tags + (propertize + (doom-modeline-lsp-icon "TAGS" 'doom-modeline-lsp-success) + 'help-echo "TAGS: Citre mode +mouse-1: Toggle citre mode" + 'mouse-face 'doom-modeline-highlight + 'local-map (make-mode-line-mouse-map 'mouse-1 #'citre-mode)))) +(add-hook 'citre-mode-hook #'doom-modeline-update-tags) + +(defun doom-modeline-update-lsp-icon () + "Update lsp icon." + (cond ((bound-and-true-p lsp-mode) + (doom-modeline-update-lsp)) + ((bound-and-true-p eglot--managed-mode) + (doom-modeline-update-eglot)) + ((bound-and-true-p citre-mode) + (doom-modeline-update-tags)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-lsp-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-lsp-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-update-lsp-icon)))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-icon val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-update-lsp-icon)))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-unicode-fallback + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-unicode-fallback val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (doom-modeline-update-lsp-icon)))))) + +(doom-modeline-def-segment lsp + "The LSP server state." + (when doom-modeline-lsp + (when-let ((icon (cond ((bound-and-true-p lsp-mode) + doom-modeline--lsp) + ((bound-and-true-p eglot--managed-mode) + doom-modeline--eglot) + ((bound-and-true-p citre-mode) + doom-modeline--tags))) + (sep (doom-modeline-spc))) + (concat + sep + (doom-modeline-display-icon icon) + sep)))) + +(defun doom-modeline-override-eglot () + "Override `eglot' mode-line." + (if (and doom-modeline-lsp + (bound-and-true-p doom-modeline-mode)) + (setq mode-line-misc-info + (delq (assq 'eglot--managed-mode mode-line-misc-info) mode-line-misc-info)) + (add-to-list 'mode-line-misc-info + `(eglot--managed-mode (" [" eglot--mode-line-format "] "))))) +(add-hook 'eglot-managed-mode-hook #'doom-modeline-override-eglot) +(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-eglot) + +(doom-modeline-add-variable-watcher + 'doom-modeline-battery + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-lsp val) + (doom-modeline-override-eglot)))) + + +;; +;; GitHub +;; + +(defvar doom-modeline--github-notification-number 0) +(defvar doom-modeline-before-github-fetch-notification-hook nil + "Hooks before fetching GitHub notifications. +Example: + (add-hook \\='doom-modeline-before-github-fetch-notification-hook + #\\='auth-source-pass-enable)") + +(defvar doom-modeline-after-github-fetch-notification-hook nil + "Hooks after fetching GitHub notifications.") + +(defun doom-modeline--github-fetch-notifications () + "Fetch GitHub notifications." + (when (and doom-modeline-github + (require 'async nil t)) + (async-start + `(lambda () + ,(async-inject-variables + "\\`\\(load-path\\|auth-sources\\|doom-modeline-before-github-fetch-notification-hook\\)\\'") + (run-hooks 'doom-modeline-before-github-fetch-notification-hook) + (when (require 'ghub nil t) + (with-timeout (10) + (ignore-errors + (when-let* ((username (ghub--username ghub-default-host)) + (token (or (ghub--token ghub-default-host username 'forge t) + (ghub--token ghub-default-host username 'ghub t)))) + (ghub-get "/notifications" + '((notifications . t)) + :host ghub-default-host + :username username + :auth token + :unpaginate t + :noerror t)))))) + (lambda (result) + (message "") ; suppress message + (setq doom-modeline--github-notification-number (length result)) + (run-hooks 'doom-modeline-after-github-fetch-notification-hook))))) + +(defvar doom-modeline--github-timer nil) +(defun doom-modeline-github-timer () + "Start/Stop the timer for GitHub fetching." + (if (timerp doom-modeline--github-timer) + (cancel-timer doom-modeline--github-timer)) + (setq doom-modeline--github-timer + (and doom-modeline-github + (run-with-idle-timer 30 + doom-modeline-github-interval + #'doom-modeline--github-fetch-notifications)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-github + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-github val) + (doom-modeline-github-timer)))) + +(doom-modeline-github-timer) + +(doom-modeline-def-segment github + "The GitHub notifications." + (when (and doom-modeline-github + (doom-modeline--segment-visible 'github) + (numberp doom-modeline--github-notification-number)) + (let ((sep (doom-modeline-spc))) + (concat + sep + (propertize + (concat + (doom-modeline-icon 'octicon "nf-oct-mark_github" "🔔" "&" + :face 'doom-modeline-notification) + (and (> doom-modeline--github-notification-number 0) (doom-modeline-vspc)) + (propertize + (cond + ((<= doom-modeline--github-notification-number 0) "") + ((> doom-modeline--github-notification-number 99) "99+") + (t (number-to-string doom-modeline--github-notification-number))) + 'face '(:inherit + (doom-modeline-unread-number doom-modeline-notification)))) + 'help-echo "Github Notifications +mouse-1: Show notifications +mouse-3: Fetch notifications" + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] + (lambda () + "Open GitHub notifications page." + (interactive) + (run-with-idle-timer 300 nil #'doom-modeline--github-fetch-notifications) + (browse-url "https://github.com/notifications"))) + (define-key map [mode-line mouse-3] + (lambda () + "Fetching GitHub notifications." + (interactive) + (message "Fetching GitHub notifications...") + (doom-modeline--github-fetch-notifications))) + map)) + sep)))) + + +;; +;; Debug states +;; + +;; Highlight the doom-modeline while debugging. +(defvar-local doom-modeline--debug-cookie nil) +(defun doom-modeline--debug-visual (&rest _) + "Update the face of mode-line for debugging." + (mapc (lambda (buffer) + (with-current-buffer buffer + (setq doom-modeline--debug-cookie + (face-remap-add-relative 'doom-modeline 'doom-modeline-debug-visual)) + (force-mode-line-update))) + (buffer-list))) + +(defun doom-modeline--normal-visual (&rest _) + "Restore the face of mode-line." + (mapc (lambda (buffer) + (with-current-buffer buffer + (when doom-modeline--debug-cookie + (face-remap-remove-relative doom-modeline--debug-cookie) + (force-mode-line-update)))) + (buffer-list))) + +(add-hook 'dap-session-created-hook #'doom-modeline--debug-visual) +(add-hook 'dap-terminated-hook #'doom-modeline--normal-visual) + +(defun doom-modeline-debug-icon (face) + "Display debug icon with FACE and ARGS." + (doom-modeline-icon 'codicon "nf-cod-debug" "🐛" "!" :face face)) + +(defun doom-modeline--debug-dap () + "The current `dap-mode' state." + (when (and (bound-and-true-p dap-mode) + (bound-and-true-p lsp-mode)) + (when-let ((session (dap--cur-session))) + (when (dap--session-running session) + (propertize (doom-modeline-debug-icon 'doom-modeline-info) + 'help-echo (format "DAP (%s - %s) +mouse-1: Display debug hydra +mouse-2: Display recent configurations +mouse-3: Disconnect session" + (dap--debug-session-name session) + (dap--debug-session-state session)) + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] + #'dap-hydra) + (define-key map [mode-line mouse-2] + #'dap-debug-recent) + (define-key map [mode-line mouse-3] + #'dap-disconnect) + map)))))) + +(defvar-local doom-modeline--debug-dap nil) +(defun doom-modeline-update-debug-dap (&rest _) + "Update dap debug state." + (setq doom-modeline--debug-dap (doom-modeline--debug-dap))) + +(add-hook 'dap-session-created-hook #'doom-modeline-update-debug-dap) +(add-hook 'dap-session-changed-hook #'doom-modeline-update-debug-dap) +(add-hook 'dap-terminated-hook #'doom-modeline-update-debug-dap) + +(defsubst doom-modeline--debug-edebug () + "The current `edebug' state." + (when (bound-and-true-p edebug-mode) + (propertize (doom-modeline-debug-icon 'doom-modeline-info) + 'help-echo (format "EDebug (%s) +mouse-1: Show help +mouse-2: Next +mouse-3: Stop debugging" + edebug-execution-mode) + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] + #'edebug-help) + (define-key map [mode-line mouse-2] + #'edebug-next-mode) + (define-key map [mode-line mouse-3] + #'edebug-stop) + map)))) + +(defsubst doom-modeline--debug-on-error () + "The current `debug-on-error' state." + (when debug-on-error + (propertize (doom-modeline-debug-icon 'doom-modeline-urgent) + 'help-echo "Debug on Error +mouse-1: Toggle Debug on Error" + 'mouse-face 'doom-modeline-highlight + 'local-map (make-mode-line-mouse-map 'mouse-1 #'toggle-debug-on-error)))) + +(defsubst doom-modeline--debug-on-quit () + "The current `debug-on-quit' state." + (when debug-on-quit + (propertize (doom-modeline-debug-icon 'doom-modeline-warning) + 'help-echo "Debug on Quit +mouse-1: Toggle Debug on Quit" + 'mouse-face 'doom-modeline-highlight + 'local-map (make-mode-line-mouse-map 'mouse-1 #'toggle-debug-on-quit)))) + +(doom-modeline-def-segment debug + "The current debug state." + (when (doom-modeline--segment-visible 'debug) + (let* ((dap doom-modeline--debug-dap) + (edebug (doom-modeline--debug-edebug)) + (on-error (doom-modeline--debug-on-error)) + (on-quit (doom-modeline--debug-on-quit)) + (vsep (doom-modeline-vspc)) + (sep (and (or dap edebug on-error on-quit) (doom-modeline-spc)))) + (concat sep + (and dap (concat dap (and (or edebug on-error on-quit) vsep))) + (and edebug (concat edebug (and (or on-error on-quit) vsep))) + (and on-error (concat on-error (and on-quit vsep))) + on-quit + sep)))) + + +;; +;; PDF pages +;; + +(defvar-local doom-modeline--pdf-pages nil) +(defun doom-modeline-update-pdf-pages () + "Update PDF pages." + (setq doom-modeline--pdf-pages + (format " P%d/%d " + (or (eval `(pdf-view-current-page)) 0) + (pdf-cache-number-of-pages)))) +(add-hook 'pdf-view-change-page-hook #'doom-modeline-update-pdf-pages) + +(doom-modeline-def-segment pdf-pages + "Display PDF pages." + doom-modeline--pdf-pages) + + +;; +;; `mu4e' notifications +;; + +(doom-modeline-def-segment mu4e + "Show notifications of any unread emails in `mu4e'." + (when (and doom-modeline-mu4e + (doom-modeline--segment-visible 'mu4e)) + (let ((sep (doom-modeline-spc)) + (vsep (doom-modeline-vspc)) + (icon (doom-modeline-icon 'mdicon "nf-md-email" "📧" "#" + :face 'doom-modeline-notification))) + (cond ((and (bound-and-true-p mu4e-alert-mode-line) + (numberp mu4e-alert-mode-line) + ;; don't display if the unread mails count is zero + (> mu4e-alert-mode-line 0)) + (concat + sep + (propertize + (concat + icon + vsep + (propertize + (if (> mu4e-alert-mode-line doom-modeline-number-limit) + (format "%d+" doom-modeline-number-limit) + (number-to-string mu4e-alert-mode-line)) + 'face '(:inherit + (doom-modeline-unread-number doom-modeline-notification)))) + 'mouse-face 'doom-modeline-highlight + 'keymap '(mode-line keymap + (mouse-1 . mu4e-alert-view-unread-mails) + (mouse-2 . mu4e-alert-view-unread-mails) + (mouse-3 . mu4e-alert-view-unread-mails)) + 'help-echo (concat (if (= mu4e-alert-mode-line 1) + "You have an unread email" + (format "You have %s unread emails" mu4e-alert-mode-line)) + "\nClick here to view " + (if (= mu4e-alert-mode-line 1) "it" "them"))) + sep)) + ((bound-and-true-p mu4e-modeline-mode) + (concat sep icon vsep + (propertize (mu4e--modeline-string) + 'face 'doom-modeline-notification) + sep)))))) + +(defun doom-modeline-override-mu4e-alert (&rest _) + "Delete `mu4e-alert-mode-line' from global modeline string." + (when (and (featurep 'mu4e-alert) + (bound-and-true-p mu4e-alert-mode-line)) + (if (and doom-modeline-mu4e + (bound-and-true-p doom-modeline-mode)) + ;; Delete original modeline + (progn + (setq global-mode-string + (delete '(:eval mu4e-alert-mode-line) global-mode-string)) + (setq mu4e-alert-modeline-formatter #'identity)) + ;; Recover default settings + (setq mu4e-alert-modeline-formatter #'mu4e-alert-default-mode-line-formatter)))) +(advice-add #'mu4e-alert-enable-mode-line-display + :after #'doom-modeline-override-mu4e-alert) +(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-mu4e-alert) + +(defun doom-modeline-override-mu4e-modeline (&rest _) + "Delete `mu4e-alert-mode-line' from global modeline string." + (when (bound-and-true-p mu4e-modeline-mode) + (if (and doom-modeline-mu4e + (bound-and-true-p doom-modeline-mode)) + ;; Delete original modeline + (setq global-mode-string + (delete mu4e--modeline-item global-mode-string)) + ;; Recover default settings + (add-to-list 'global-mode-string mu4e--modeline-item)))) +(add-hook 'mu4e-modeline-mode-hook #'doom-modeline-override-mu4e-modeline) +(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-mu4e-modeline) + +(doom-modeline-add-variable-watcher + 'doom-modeline-mu4e + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-mu4e val) + (doom-modeline-override-mu4e-alert) + (doom-modeline-override-mu4e-modeline)))) + + +;; +;; `gnus' notifications +;; + +(defvar doom-modeline--gnus-unread-mail 0) +(defvar doom-modeline--gnus-started nil + "Used to determine if gnus has started.") +(defun doom-modeline-update-gnus-status (&rest _) + "Get the total number of unread news of gnus group." + (setq doom-modeline--gnus-unread-mail + (when (and doom-modeline-gnus + doom-modeline--gnus-started) + (let ((total-unread-news-number 0)) + (mapc (lambda (g) + (let* ((group (car g)) + (unread (eval `(gnus-group-unread ,group)))) + (when (and (not (seq-contains-p doom-modeline-gnus-excluded-groups group)) + (numberp unread) + (> unread 0)) + (setq total-unread-news-number (+ total-unread-news-number unread))))) + gnus-newsrc-alist) + total-unread-news-number)))) + +;; Update the modeline after changes have been made +(add-hook 'gnus-group-update-hook #'doom-modeline-update-gnus-status) +(add-hook 'gnus-summary-update-hook #'doom-modeline-update-gnus-status) +(add-hook 'gnus-group-update-group-hook #'doom-modeline-update-gnus-status) +(add-hook 'gnus-after-getting-new-news-hook #'doom-modeline-update-gnus-status) + +;; Only start to listen to gnus when gnus is actually running +(defun doom-modeline-start-gnus-listener () + "Start GNUS listener." + (when (and doom-modeline-gnus + (not doom-modeline--gnus-started)) + (setq doom-modeline--gnus-started t) + ;; Scan gnus in the background if the timer is higher than 0 + (doom-modeline-update-gnus-status) + (if (> doom-modeline-gnus-timer 0) + (gnus-demon-add-handler 'gnus-demon-scan-news doom-modeline-gnus-timer doom-modeline-gnus-idle)))) +(add-hook 'gnus-started-hook #'doom-modeline-start-gnus-listener) + +;; Stop the listener if gnus isn't running +(defun doom-modeline-stop-gnus-listener () + "Stop GNUS listener." + (setq doom-modeline--gnus-started nil)) +(add-hook 'gnus-exit-gnus-hook #'doom-modeline-stop-gnus-listener) + +(doom-modeline-def-segment gnus + "Show notifications of any unread emails in `gnus'." + (when (and (doom-modeline--segment-visible 'gnus) + doom-modeline-gnus + doom-modeline--gnus-started + ;; Don't display if the unread mails count is zero + (numberp doom-modeline--gnus-unread-mail) + (> doom-modeline--gnus-unread-mail 0)) + (let ((sep (doom-modeline-spc)) + (vsep (doom-modeline-vspc))) + (concat + sep + (propertize + (concat + (doom-modeline-icon 'mdicon "nf-md-email" "📧" "#" + :face 'doom-modeline-notification) + vsep + (propertize + (if (> doom-modeline--gnus-unread-mail doom-modeline-number-limit) + (format "%d+" doom-modeline-number-limit) + (number-to-string doom-modeline--gnus-unread-mail)) + 'face '(:inherit + (doom-modeline-unread-number doom-modeline-notification)))) + 'mouse-face 'doom-modeline-highlight + 'help-echo (if (= doom-modeline--gnus-unread-mail 1) + "You have an unread email" + (format "You have %s unread emails" doom-modeline--gnus-unread-mail))) + sep)))) + + +;; +;; IRC notifications +;; + +(defun doom-modeline--shorten-irc (name) + "Wrapper for `tracking-shorten' and `erc-track-shorten-function' with NAME. + +One key difference is that when `tracking-shorten' and +`erc-track-shorten-function' returns nil we will instead return the original +value of name. This is necessary in cases where the user has stylized the name +to be an icon and we don't want to remove that so we just return the original." + (or (and (boundp 'tracking-shorten) + (car (tracking-shorten (list name)))) + (and (boundp 'erc-track-shorten-function) + (functionp erc-track-shorten-function) + (car (funcall erc-track-shorten-function (list name)))) + (and (boundp 'rcirc-short-buffer-name) + (rcirc-short-buffer-name name)) + name)) + +(defun doom-modeline--tracking-buffers (buffers) + "Logic to convert some irc BUFFERS to their font-awesome icon." + (mapconcat + (lambda (b) + (propertize + (doom-modeline--shorten-irc (funcall doom-modeline-irc-stylize b)) + 'face '(:inherit (doom-modeline-unread-number doom-modeline-notification)) + 'help-echo (format "IRC Notification: %s\nmouse-1: Switch to buffer" b) + 'mouse-face 'doom-modeline-highlight + 'local-map (make-mode-line-mouse-map + 'mouse-1 + (lambda () + (interactive) + (when (buffer-live-p (get-buffer b)) + (switch-to-buffer b)))))) + buffers + (doom-modeline-vspc))) + +(defun doom-modeline--circe-p () + "Check if `circe' is in use." + (boundp 'tracking-mode-line-buffers)) + +(defun doom-modeline--erc-p () + "Check if `erc' is in use." + (boundp 'erc-modified-channels-alist)) + +(defun doom-modeline--rcirc-p () + "Check if `rcirc' is in use." + (bound-and-true-p rcirc-track-minor-mode)) + +(defun doom-modeline--get-buffers () + "Gets the buffers that have activity." + (cond + ((doom-modeline--circe-p) + tracking-buffers) + ((doom-modeline--erc-p) + (mapcar (lambda (l) + (buffer-name (car l))) + erc-modified-channels-alist)) + ((doom-modeline--rcirc-p) + (mapcar (lambda (b) + (buffer-name b)) + rcirc-activity)))) + +;; Create a modeline segment that contains all the irc tracked buffers +(doom-modeline-def-segment irc-buffers + "The list of shortened, unread irc buffers." + (when (and doom-modeline-irc + (doom-modeline--segment-visible 'irc-buffers)) + (let* ((buffers (doom-modeline--get-buffers)) + (number (length buffers)) + (sep (doom-modeline-spc))) + (when (> number 0) + (concat + sep + (doom-modeline--tracking-buffers buffers) + sep))))) + +(doom-modeline-def-segment irc + "A notification icon for any unread irc buffer." + (when (and doom-modeline-irc + (doom-modeline--segment-visible 'irc)) + (let* ((buffers (doom-modeline--get-buffers)) + (number (length buffers)) + (sep (doom-modeline-spc)) + (vsep (doom-modeline-vspc))) + (when (> number 0) + (concat + sep + + (propertize (concat + (doom-modeline-icon 'mdicon "nf-md-message_processing" "🗊" "#" + :face 'doom-modeline-notification) + vsep + ;; Display the number of unread buffers + (propertize (number-to-string number) + 'face '(:inherit + (doom-modeline-unread-number + doom-modeline-notification)))) + 'help-echo (format "IRC Notifications: %s\n%s" + (mapconcat + (lambda (b) (funcall doom-modeline-irc-stylize b)) + buffers + ", ") + (cond + ((doom-modeline--circe-p) + "mouse-1: Switch to previous unread buffer +mouse-3: Switch to next unread buffer") + ((doom-modeline--erc-p) + "mouse-1: Switch to buffer +mouse-3: Switch to next unread buffer") + ((doom-modeline--rcirc-p) + "mouse-1: Switch to server buffer +mouse-3: Switch to next unread buffer"))) + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (cond + ((doom-modeline--circe-p) + (define-key map [mode-line mouse-1] + #'tracking-previous-buffer) + (define-key map [mode-line mouse-3] + #'tracking-next-buffer)) + ((doom-modeline--erc-p) + (define-key map [mode-line mouse-1] + #'erc-switch-to-buffer) + (define-key map [mode-line mouse-3] + #'erc-track-switch-buffer)) + ((doom-modeline--rcirc-p) + (define-key map [mode-line mouse-1] + #'rcirc-switch-to-server-buffer) + (define-key map [mode-line mouse-3] + #'rcirc-next-active-buffer))) + map)) + + ;; Display the unread irc buffers as well + (when doom-modeline-irc-buffers + (concat sep (doom-modeline--tracking-buffers buffers))) + + sep))))) + +(defun doom-modeline-override-rcirc () + "Override default `rcirc' mode-line." + (if (and doom-modeline-irc + (bound-and-true-p doom-modeline-mode)) + (setq global-mode-string + (delq 'rcirc-activity-string global-mode-string)) + (when (and rcirc-track-minor-mode + (not (memq 'rcirc-activity-string global-mode-string))) + (setq global-mode-string + (append global-mode-string '(rcirc-activity-string)))))) +(add-hook 'rcirc-track-minor-mode-hook #'doom-modeline-override-rcirc) +(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-rcirc) + +(doom-modeline-add-variable-watcher + 'doom-modeline-irc + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-irc val) + (doom-modeline-override-rcirc)))) + + +;; +;; Battery status +;; + +(defun doom-modeline-battery-icon (icon unicode text face) + "Displays the battery ICON with FACE. + +UNICODE and TEXT are fallbacks. +Uses `nerd-icons-mdicon' to fetch the icon." + (doom-modeline-icon 'mdicon icon unicode text :face face)) + +(defvar doom-modeline--battery-status nil) +(defun doom-modeline-update-battery-status () + "Update battery status." + (setq doom-modeline--battery-status + (when (and doom-modeline-battery + (bound-and-true-p display-battery-mode)) + (let* ((data (and battery-status-function + (functionp battery-status-function) + (funcall battery-status-function))) + (status (cdr (assoc ?L data))) + (charging? (or (string-equal "AC" status) + (string-equal "on-line" status))) + (percentage (car (read-from-string (or (cdr (assq ?p data)) "ERR")))) + (valid-percentage? (and (numberp percentage) + (>= percentage 0) + (<= percentage battery-mode-line-limit))) + (face (if valid-percentage? + (cond (charging? 'doom-modeline-battery-charging) + ((< percentage battery-load-critical) 'doom-modeline-battery-critical) + ((< percentage 25) 'doom-modeline-battery-warning) + ((< percentage 95) 'doom-modeline-battery-normal) + (t 'doom-modeline-battery-full)) + 'doom-modeline-battery-error)) + (icon (if valid-percentage? + (cond + ((>= percentage 100) + (doom-modeline-battery-icon (if charging? + "nf-md-battery_charging_100" + "nf-md-battery") + "🔋" "-" face)) + ((>= percentage 90) + (doom-modeline-battery-icon (if charging? + "nf-md-battery_charging_90" + "nf-md-battery_90") + "🔋" "-" face)) + ((>= percentage 80) + (doom-modeline-battery-icon (if charging? + "nf-md-battery_charging_80" + "nf-md-battery_80") + "🔋" "-" face)) + ((>= percentage 70) + (doom-modeline-battery-icon (if charging? + "nf-md-battery_charging_70" + "nf-md-battery_70") + "🔋" "-" face)) + ((>= percentage 60) + (doom-modeline-battery-icon (if charging? + "nf-md-battery_charging_60" + "nf-md-battery_60") + "🔋" "-" face)) + ((>= percentage 50) + (doom-modeline-battery-icon (if charging? + "nf-md-battery_charging_50" + "nf-md-battery_50") + "🔋" "-" face)) + ((>= percentage 40) + (doom-modeline-battery-icon (if charging? + "nf-md-battery_charging_40" + "nf-md-battery_40") + "🔋" "-" face)) + ((>= percentage 30) + (doom-modeline-battery-icon (if charging? + "nf-md-battery_charging_30" + "nf-md-battery_30") + "🔋" "-" face)) + ((>= percentage 20) + (doom-modeline-battery-icon (if charging? + "nf-md-battery_charging_20" + "nf-md-battery_20") + "🔋" "-" face)) + ((>= percentage 10) + (doom-modeline-battery-icon (if charging? + "nf-md-battery_charging_10" + "nf-md-battery_10") + "🪫" "-" face)) + (t (doom-modeline-battery-icon (if charging? + "nf-md-battery_charging_outline" + "nf-md-battery_outline") + "🪫" "!" face))) + (doom-modeline-battery-icon "nf-md-battery_alert" "⚠" "N/A" face))) + (text (if valid-percentage? (format "%d%s" percentage "%%") "")) + (help-echo (if (and battery-echo-area-format data valid-percentage?) + (battery-format battery-echo-area-format data) + "Battery status not available"))) + (cons (propertize icon 'help-echo help-echo) + (propertize text 'face face 'help-echo help-echo)))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-icon + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-icon val) + (doom-modeline-update-battery-status)))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-unicode-fallback + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-unicode-fallback val) + (doom-modeline-update-battery-status)))) + +(doom-modeline-def-segment battery + "Display battery status." + (when (and doom-modeline-battery + (bound-and-true-p display-battery-mode) + (doom-modeline--segment-visible 'battery)) + (let ((sep (doom-modeline-spc)) + (vsep (doom-modeline-vspc))) + (concat sep + (car doom-modeline--battery-status) + vsep + (cdr doom-modeline--battery-status) + sep)))) + +(defun doom-modeline-override-battery () + "Override default battery mode-line." + (if (and doom-modeline-battery + (bound-and-true-p doom-modeline-mode)) + (progn + (advice-add #'battery-update :override #'doom-modeline-update-battery-status) + (setq global-mode-string + (delq 'battery-mode-line-string global-mode-string)) + (and (bound-and-true-p display-battery-mode) (battery-update))) + (progn + (advice-remove #'battery-update #'doom-modeline-update-battery-status) + (when (and display-battery-mode battery-status-function battery-mode-line-format + (not (memq 'battery-mode-line-string global-mode-string))) + (setq global-mode-string + (append global-mode-string '(battery-mode-line-string))))))) +(add-hook 'display-battery-mode-hook #'doom-modeline-override-battery) +(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-battery) + +(doom-modeline-add-variable-watcher + 'doom-modeline-battery + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-battery val) + (doom-modeline-override-battery)))) + + +;; +;; Package information +;; + +(doom-modeline-def-segment package + "Show package information via `paradox'." + (concat + (doom-modeline-display-text + (format-mode-line 'mode-line-front-space)) + + (when (and doom-modeline-icon doom-modeline-major-mode-icon) + (concat + (doom-modeline-spc) + (doom-modeline-icon 'faicon "nf-fa-archive" nil nil + :face (doom-modeline-face + (if doom-modeline-major-mode-color-icon + 'nerd-icons-silver + 'mode-line))))) + (doom-modeline-display-text + (format-mode-line 'mode-line-buffer-identification)))) + + +;; +;; Helm +;; + +(defvar doom-modeline--helm-buffer-ids + '(("*helm*" . "HELM") + ("*helm M-x*" . "HELM M-x") + ("*swiper*" . "SWIPER") + ("*Projectile Perspectives*" . "HELM Projectile Perspectives") + ("*Projectile Layouts*" . "HELM Projectile Layouts") + ("*helm-ag*" . (lambda () + (format "HELM Ag: Using %s" + (car (split-string helm-ag-base-command)))))) + "Alist of custom helm buffer names to use. +The cdr can also be a function that returns a name to use.") + +(doom-modeline-def-segment helm-buffer-id + "Helm session identifier." + (when (bound-and-true-p helm-alive-p) + (let ((sep (doom-modeline-spc))) + (concat + sep + (when doom-modeline-icon + (concat + (doom-modeline-icon 'sucicon "nf-custom-emacs" nil nil + :face (doom-modeline-face + (and doom-modeline-major-mode-color-icon + 'nerd-icons-blue))) + sep)) + (propertize + (let ((custom (cdr (assoc (buffer-name) doom-modeline--helm-buffer-ids))) + (case-fold-search t) + (name (replace-regexp-in-string "-" " " (buffer-name)))) + (cond ((stringp custom) custom) + ((functionp custom) (funcall custom)) + (t + (string-match "\\*helm:? \\(mode \\)?\\([^\\*]+\\)\\*" name) + (concat "HELM " (capitalize (match-string 2 name)))))) + 'face (doom-modeline-face 'doom-modeline-buffer-file)) + sep)))) + +(doom-modeline-def-segment helm-number + "Number of helm candidates." + (when (bound-and-true-p helm-alive-p) + (concat + (propertize (format " %d/%d" + (helm-candidate-number-at-point) + (helm-get-candidate-number t)) + 'face (doom-modeline-face 'doom-modeline-buffer-path)) + (propertize (format " (%d total) " (helm-get-candidate-number)) + 'face (doom-modeline-face 'doom-modeline-info))))) + +(doom-modeline-def-segment helm-help + "Helm keybindings help." + (when (bound-and-true-p helm-alive-p) + (mapcar + (lambda (s) + (if (string-prefix-p "\\<" s) + (propertize (substitute-command-keys s) + 'face (doom-modeline-face + 'doom-modeline-buffer-file)) + s)) + '("\\<helm-map>\\[helm-help]" "(help) " + "\\<helm-map>\\[helm-select-action]" "(actions) " + "\\<helm-map>\\[helm-maybe-exit-minibuffer]/F1/F2..." "(action) ")))) + +(doom-modeline-def-segment helm-prefix-argument + "Helm prefix argument." + (when (and (bound-and-true-p helm-alive-p) + helm--mode-line-display-prefarg) + (let ((arg (prefix-numeric-value (or prefix-arg current-prefix-arg)))) + (unless (= arg 1) + (propertize (format "C-u %s" arg) + 'face (doom-modeline-face 'doom-modeline-info)))))) + +(defvar doom-modeline--helm-current-source nil + "The currently active helm source.") +(doom-modeline-def-segment helm-follow + "Helm follow indicator." + (and (bound-and-true-p helm-alive-p) + doom-modeline--helm-current-source + (eq 1 (cdr (assq 'follow doom-modeline--helm-current-source))) + "HF")) + +;; +;; Git timemachine +;; + +(doom-modeline-def-segment git-timemachine + (concat + (doom-modeline-spc) + (doom-modeline--buffer-mode-icon) + (doom-modeline--buffer-state-icon) + (propertize + "*%b*" + 'face (doom-modeline-face 'doom-modeline-buffer-timemachine)))) + +;; +;; Markdown/Org preview +;; + +(doom-modeline-def-segment grip + (when (bound-and-true-p grip-mode) + (let ((sep (doom-modeline-spc))) + (concat + sep + (let ((face (doom-modeline-face + (if grip--process + (pcase (process-status grip--process) + ('run 'doom-modeline-info) + ('exit 'doom-modeline-warning) + (_ 'doom-modeline-urgent)) + 'doom-modeline-urgent)))) + (propertize + (doom-modeline-icon 'codicon "nf-cod-open_preview" "🗐" "@" :face face) + 'help-echo (format "Preview on %s +mouse-1: Preview in browser +mouse-2: Stop preview +mouse-3: Restart preview" + (grip--preview-url)) + 'mouse-face 'doom-modeline-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] + #'grip-browse-preview) + (define-key map [mode-line mouse-2] + #'grip-stop-preview) + (define-key map [mode-line mouse-3] + #'grip-restart-preview) + map))) + sep)))) + +;; +;; Follow mode +;; + +(doom-modeline-def-segment follow + (when (bound-and-true-p follow-mode) + (let* ((windows (follow-all-followers)) + (nwindows (length windows)) + (nfollowing (- (length (memq (selected-window) windows)) 1))) + (concat + (doom-modeline-spc) + (propertize (format "Follow %d/%d" (- nwindows nfollowing) nwindows) + 'face 'doom-modeline-buffer-minor-mode))))) + +;; +;; Display time +;; + +(defconst doom-modeline--clock-hour-hand-ratio 0.45 + "Length of the hour hand as a proportion of the radius.") + +(defconst doom-modeline--clock-minute-hand-ratio 0.7 + "Length of the minute hand as a proportion of the radius.") + +(defun doom-modeline--create-clock-svg (hour minute radius color) + "Construct an SVG clock showing the time HOUR:MINUTE. +The clock will be of the specified RADIUS and COLOR." + (let ((thickness-factor (image-compute-scaling-factor 'auto)) + (hour-x (* radius (sin (* (- 6 hour (/ minute 60.0)) (/ float-pi 6))) + doom-modeline--clock-hour-hand-ratio)) + (hour-y (* radius (cos (* (- 6 hour (/ minute 60.0)) (/ float-pi 6))) + doom-modeline--clock-hour-hand-ratio)) + (minute-x (* radius (sin (* (- 30 minute) (/ float-pi 30))) + doom-modeline--clock-minute-hand-ratio)) + (minute-y (* radius (cos (* (- 30 minute) (/ float-pi 30))) + doom-modeline--clock-minute-hand-ratio)) + (svg (svg-create (* 2 radius) (* 2 radius) :stroke color))) + (svg-circle svg radius radius (- radius thickness-factor) + :fill "none" :stroke-width (* 2 thickness-factor)) + (svg-circle svg radius radius thickness-factor + :fill color :stroke "none") + (svg-line svg radius radius (+ radius hour-x) (+ radius hour-y) + :stroke-width (* 2 thickness-factor)) + (svg-line svg radius radius (+ radius minute-x) (+ radius minute-y) + :stroke-width (* 1.5 thickness-factor)) + svg)) + +(defvar doom-modeline--clock-cache nil + "The last result of `doom-modeline--generate-clock'.") + +(defun doom-modeline--generate-clock () + "Return a string containing the current time as an analogue clock svg. +When the svg library is not available, return nil." + (cdr + (or (and (equal (truncate (float-time) + (* doom-modeline-time-clock-minute-resolution 60)) + (car doom-modeline--clock-cache)) + doom-modeline--clock-cache) + (and (require 'svg nil t) + (setq doom-modeline--clock-cache + (cons (truncate (float-time) + (* doom-modeline-time-clock-minute-resolution 60)) + (propertize + " " + 'display + (svg-image + (doom-modeline--create-clock-svg + (string-to-number (format-time-string "%-I")) ; hour + (* (truncate (string-to-number (format-time-string "%-M")) + doom-modeline-time-clock-minute-resolution) + doom-modeline-time-clock-minute-resolution) ; minute + (if (integerp doom-modeline-time-clock-size) ; radius + doom-modeline-time-clock-size + (* doom-modeline-height 0.5 doom-modeline-time-clock-size)) + "currentColor") + :scale 1 :ascent 'center) + 'face 'doom-modeline-time + 'help-echo (lambda (_window _object _pos) + (format-time-string "%c"))))))))) + +(defun doom-modeline-time-icon () + "Displays the time icon." + (or (and doom-modeline-time-live-icon + doom-modeline-time-analogue-clock + (display-graphic-p) + (doom-modeline--generate-clock)) + (doom-modeline-icon + 'mdicon + (if doom-modeline-time-live-icon + (pcase (% (caddr (decode-time)) 12) + (0 "nf-md-clock_time_twelve_outline") + (1 "nf-md-clock_time_one_outline") + (2 "nf-md-clock_time_two_outline") + (3 "nf-md-clock_time_three_outline") + (4 "nf-md-clock_time_four_outline") + (5 "nf-md-clock_time_five_outline") + (6 "nf-md-clock_time_six_outline") + (7 "nf-md-clock_time_seven_outline") + (8 "nf-md-clock_time_eight_outline") + (9 "nf-md-clock_time_nine_outline") + (10 "nf-md-clock_time_ten_outline") + (11 "nf-md-clock_time_eleven_outline")) + "nf-md-clock_outline") + "⏰" + "" + :face '(:inherit doom-modeline-time :weight normal)))) + +(doom-modeline-def-segment time + (when (and doom-modeline-time + (bound-and-true-p display-time-mode) + (doom-modeline--segment-visible 'time)) + (concat + (doom-modeline-spc) + (when doom-modeline-time-icon + (concat + (doom-modeline-time-icon) + (and (or doom-modeline-icon doom-modeline-unicode-fallback) + (doom-modeline-vspc)))) + (propertize display-time-string + 'face (doom-modeline-face 'doom-modeline-time))))) + +(defun doom-modeline-override-time () + "Override default `display-time' mode-line." + (or global-mode-string (setq global-mode-string '(""))) + (if (and doom-modeline-time + (bound-and-true-p doom-modeline-mode)) + (setq global-mode-string (delq 'display-time-string global-mode-string)) + (setq global-mode-string (append global-mode-string '(display-time-string))))) +(add-hook 'display-time-mode-hook #'doom-modeline-override-time) +(add-hook 'doom-modeline-mode-hook #'doom-modeline-override-time) + +(doom-modeline-add-variable-watcher + 'doom-modeline-time + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-time val) + (doom-modeline-override-time)))) + +;; +;; Compilation +;; + +(doom-modeline-def-segment compilation + (and (bound-and-true-p compilation-in-progress) + (propertize "[Compiling] " + 'face (doom-modeline-face 'doom-modeline-compilation) + 'help-echo "Compiling; mouse-2: Goto Buffer" + 'mouse-face 'doom-modeline-highlight + 'local-map + (make-mode-line-mouse-map + 'mouse-2 + #'compilation-goto-in-progress-buffer)))) + +;; +;; Eldoc +;; + +(doom-modeline-def-segment eldoc + (and (bound-and-true-p eldoc-mode) + '(eldoc-mode-line-string + (" " eldoc-mode-line-string " ")))) + +(defun doom-modeline-eldoc-minibuffer-message (format-string &rest args) + "Display message specified by FORMAT-STRING and ARGS on the mode-line as needed. +This function displays the message produced by formatting ARGS +with FORMAT-STRING on the mode line when the current buffer is a minibuffer. +Otherwise, it displays the message like `message' would." + (if (minibufferp) + (progn + (add-hook 'minibuffer-exit-hook + (lambda () (setq eldoc-mode-line-string nil + ;; https://debbugs.gnu.org/16920 + eldoc-last-message nil)) + nil t) + (with-current-buffer + (window-buffer + (or (window-in-direction 'above (minibuffer-window)) + (minibuffer-selected-window) + (get-largest-window))) + (setq eldoc-mode-line-string + (when (stringp format-string) + (apply #'format-message format-string args))) + (force-mode-line-update))) + (apply #'message format-string args))) + +;; +;; Kubernetes +;; + +(doom-modeline-def-segment k8s + (when (and (bound-and-true-p kele-mode) (doom-modeline--segment-visible 'k8s)) + (let* ((ctx (kele-current-context-name :wait nil)) + (ns (kele-current-namespace :wait nil)) + (icon (doom-modeline-icon 'mdicon "nf-md-kubernetes" "K8s:" "K8s:")) + (sep (doom-modeline-spc)) + (help-msg (let ((msgs (list (format "Current context: %s" ctx)))) + (when ns + (setq msgs (append msgs (list (format "Current namespace: %s" ns))))) + (string-join msgs "\n")))) + (propertize (concat + icon sep ctx + (when (and doom-modeline-k8s-show-namespace ns) (format "(%s)" ns)) + sep) + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line down-mouse-1] kele-menu-map) + map) + 'mouse-face 'doom-modeline-highlight + 'help-echo help-msg)))) + +(provide 'doom-modeline-segments) + +;;; doom-modeline-segments.el ends here diff --git a/emacs/elpa/doom-modeline-20240605.628/doom-modeline-segments.elc b/emacs/elpa/doom-modeline-20240605.628/doom-modeline-segments.elc Binary files differ. diff --git a/emacs/elpa/doom-modeline-20240510.144/doom-modeline.el b/emacs/elpa/doom-modeline-20240605.628/doom-modeline.el diff --git a/emacs/elpa/doom-modeline-20240510.144/doom-modeline.elc b/emacs/elpa/doom-modeline-20240605.628/doom-modeline.elc Binary files differ. diff --git a/emacs/elpa/embark-20240419.452/embark-pkg.el b/emacs/elpa/embark-20240419.452/embark-pkg.el @@ -1,15 +0,0 @@ -(define-package "embark" "20240419.452" "Conveniently act on minibuffer completions" - '((emacs "27.1") - (compat "29.1.4.0")) - :commit "195add1f1ccd1059472c9df7334c97c4d155425e" :authors - '(("Omar Antolín Camarena" . "omar@matem.unam.mx")) - :maintainers - '(("Omar Antolín Camarena" . "omar@matem.unam.mx")) - :maintainer - '("Omar Antolín Camarena" . "omar@matem.unam.mx") - :keywords - '("convenience") - :url "https://github.com/oantolin/embark") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/embark-20240419.452/embark.el b/emacs/elpa/embark-20240419.452/embark.el @@ -1,4604 +0,0 @@ -;;; embark.el --- Conveniently act on minibuffer completions -*- lexical-binding: t; -*- - -;; Copyright (C) 2021-2023 Free Software Foundation, Inc. - -;; Author: Omar Antolín Camarena <omar@matem.unam.mx> -;; Maintainer: Omar Antolín Camarena <omar@matem.unam.mx> -;; Keywords: convenience -;; Version: 1.1 -;; Homepage: https://github.com/oantolin/embark -;; Package-Requires: ((emacs "27.1") (compat "29.1.4.0")) - -;; This file is part of GNU Emacs. - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see <https://www.gnu.org/licenses/>. - -;;; Commentary: - -;; This package provides a sort of right-click contextual menu for -;; Emacs, accessed through the `embark-act' command (which you should -;; bind to a convenient key), offering you relevant actions to use on -;; a target determined by the context: - -;; - In the minibuffer, the target is the current best completion -;; candidate. -;; - In the `*Completions*' buffer the target is the completion at point. -;; - In a regular buffer, the target is the region if active, or else the -;; file, symbol or url at point. - -;; The type of actions offered depend on the type of the target: - -;; - For files you get offered actions like deleting, copying, -;; renaming, visiting in another window, running a shell command on the -;; file, etc. -;; - For buffers the actions include switching to or killing the buffer. -;; - For package names the actions include installing, removing or -;; visiting the homepage. - -;; Everything is easily configurable: determining the current target, -;; classifying it, and deciding with actions are offered for each type -;; in the classification. The above introduction just mentions part of -;; the default configuration. - -;; Configuring which actions are offered for a type is particularly -;; easy and requires no programming: the `embark-keymap-alist' -;; variable associates target types with variable containing keymaps, -;; and those keymaps containing binds for the actions. For example, -;; in the default configuration the type `file' is associated with the -;; symbol `embark-file-map'. That symbol names a keymap with -;; single-letter key bindings for common Emacs file commands, for -;; instance `c' is bound to `copy-file'. This means that if while you -;; are in the minibuffer after running a command that prompts for a -;; file, such as `find-file' or `rename-file', you can copy a file by -;; running `embark-act' and then pressing `c'. - -;; These action keymaps are very convenient but not strictly necessary -;; when using `embark-act': you can use any command that reads from the -;; minibuffer as an action and the target of the action will be inserted -;; at the first minibuffer prompt. After running `embark-act' all of your -;; key bindings and even `execute-extended-command' can be used to run a -;; command. The action keymaps are normal Emacs keymaps and you should -;; feel free to bind in them whatever commands you find useful as actions. - -;; The actions in `embark-general-map' are available no matter what -;; type of completion you are in the middle of. By default this -;; includes bindings to save the current candidate in the kill ring -;; and to insert the current candidate in the previously selected -;; buffer (the buffer that was current when you executed a command -;; that opened up the minibuffer). - -;; You can read about the Embark GitHub project wiki: -;; https://github.com/oantolin/embark/wiki/Default-Actions - -;; Besides acting individually on targets, Embark lets you work -;; collectively on a set of target candidates. For example, while -;; you are in the minibuffer the candidates are simply the possible -;; completions of your input. Embark provides three commands to work -;; on candidate sets: - -;; - The `embark-act-all' command runs the same action on each of the -;; current candidates. It is just like using `embark-act' on each -;; candidate in turn. - -;; - The `embark-collect' command produces a buffer listing all -;; candidates, for you to peruse and run actions on at your leisure. -;; The candidates are displayed as a list showing additional -;; annotations. - -;; - The `embark-export' command tries to open a buffer in an -;; appropriate major mode for the set of candidates. If the -;; candidates are files export produces a Dired buffer; if they are -;; buffers, you get an Ibuffer buffer; and if they are packages you -;; get a buffer in package menu mode. - -;; These are always available as "actions" (although they do not act -;; on just the current target but on all candidates) for embark-act -;; and are bound to A, S (for "snapshot") and E, respectively, in -;; embark-general-map. This means that you do not have to bind your -;; own key bindings for these (although you can, of course), just a -;; key binding for `embark-act'. - -;;; Code: - -(require 'compat) -(eval-when-compile (require 'subr-x)) - -(require 'ffap) ; used to recognize file and url targets - -;;; User facing options - -(defgroup embark nil - "Emacs Mini-Buffer Actions Rooted in Keymaps." - :link '(info-link :tag "Info Manual" "(embark)") - :link '(url-link :tag "Homepage" "https://github.com/oantolin/embark") - :link '(emacs-library-link :tag "Library Source" "embark.el") - :group 'minibuffer - :prefix "embark-") - -(defcustom embark-keymap-alist - '((file embark-file-map) - (library embark-library-map) - (environment-variables embark-file-map) ; they come up in file completion - (url embark-url-map) - (email embark-email-map) - (buffer embark-buffer-map) - (tab embark-tab-map) - (expression embark-expression-map) - (identifier embark-identifier-map) - (defun embark-defun-map) - (symbol embark-symbol-map) - (face embark-face-map) - (command embark-command-map) - (variable embark-variable-map) - (function embark-function-map) - (minor-mode embark-command-map) - (unicode-name embark-unicode-name-map) - (package embark-package-map) - (bookmark embark-bookmark-map) - (region embark-region-map) - (sentence embark-sentence-map) - (paragraph embark-paragraph-map) - (kill-ring embark-kill-ring-map) - (heading embark-heading-map) - (flymake embark-flymake-map) - (smerge smerge-basic-map embark-general-map) - (t embark-general-map)) - "Alist of action types and corresponding keymaps. -The special key t is associated with the default keymap to use. -Each value can be either a single symbol whose value is a keymap, -or a list of such symbols." - :type '(alist :key-type (symbol :tag "Target type") - :value-type (choice (variable :tag "Keymap") - (repeat :tag "Keymaps" variable)))) - -(defcustom embark-target-finders - '(embark-target-top-minibuffer-candidate - embark-target-active-region - embark-target-collect-candidate - embark-target-completion-list-candidate - embark-target-text-heading-at-point - embark-target-bug-reference-at-point - embark-target-flymake-at-point - embark-target-smerge-at-point - embark-target-package-at-point - embark-target-email-at-point - embark-target-url-at-point - embark-target-file-at-point - embark-target-custom-variable-at-point - embark-target-identifier-at-point - embark-target-guess-file-at-point - embark-target-expression-at-point - embark-target-sentence-at-point - embark-target-paragraph-at-point - embark-target-defun-at-point - embark-target-prog-heading-at-point) - "List of functions to determine the target in current context. -Each function should take no arguments and return one of: - -1. a cons (TYPE . TARGET) where TARGET is a string and TYPE is a - symbol (which is looked up in `embark-keymap-alist' to - determine which additional keybindings for actions to setup); - -2. a dotted list of the form (TYPE TARGET START . END), where - START and END are the buffer positions bounding TARGET, used - for highlighting; or - -3. a possibly empty list of targets, each of type 1 or 2 (in - particular if a target finder does not find any targets, it - should return nil)." - :type 'hook) - -(defcustom embark-transformer-alist - '((minor-mode . embark--lookup-lighter-minor-mode) - (embark-keybinding . embark--keybinding-command) - (project-file . embark--project-file-full-path) - (package . embark--remove-package-version) - (multi-category . embark--refine-multi-category) - (file . embark--simplify-path)) - "Alist associating type to functions for transforming targets. -Each function should take a type and a target string and return a -pair of the form a `cons' of the new type and the new target." - :type '(alist :key-type symbol :value-type function)) - -(defcustom embark-become-keymaps - '(embark-become-help-map - embark-become-file+buffer-map - embark-become-shell-command-map - embark-become-match-map) - "List of keymaps for `embark-become'. -Each keymap groups a set of related commands that can -conveniently become one another." - :type '(repeat variable)) - -(defcustom embark-prompter 'embark-keymap-prompter - "Function used to prompt the user for actions. -This should be set to a function that prompts the use for an -action and returns the symbol naming the action command. The -default value, `embark-keymap-prompter' activates the type -specific action keymap given in `embark-keymap-alist'. -There is also `embark-completing-read-prompter' which -prompts for an action with completion." - :type '(choice (const :tag "Use action keymaps" embark-keymap-prompter) - (const :tag "Read action with completion" - embark-completing-read-prompter) - (function :tag "Other"))) - -(defcustom embark-keymap-prompter-key "@" - "Key to switch to the keymap prompter from `embark-completing-read-prompter'. - -The key must be either nil or a string. The -string must be accepted by `key-valid-p'." - :type '(choice key (const :tag "None" nil))) - -(defcustom embark-cycle-key nil - "Key used for `embark-cycle'. - -If the key is set to nil it defaults to the global binding of -`embark-act'. The key must be a string which is accepted by -`key-valid-p'." - :type '(choice key (const :tag "Use embark-act key" nil))) - -(defcustom embark-help-key "C-h" - "Key used for help. - -The key must be either nil or a string. The -string must be accepted by `key-valid-p'." - :type '(choice (const "C-h") - (const "?") - (const :tag "None" nil) - key)) - -(defcustom embark-keybinding-repeat - (propertize "*" 'face 'embark-keybinding-repeat) - "Indicator string for repeatable keybindings. -Keybindings are formatted by the `completing-read' prompter and -the verbose indicator." - :type 'string) - -(defface embark-keybinding-repeat - '((t :inherit font-lock-builtin-face)) - "Face used to indicate keybindings as repeatable.") - -(defface embark-keybinding '((t :inherit success)) - "Face used to display key bindings. -Used by `embark-completing-read-prompter' and `embark-keymap-help'.") - -(defface embark-keymap '((t :slant italic)) - "Face used to display keymaps. -Used by `embark-completing-read-prompter' and `embark-keymap-help'.") - -(defface embark-target '((t :inherit highlight)) - "Face used to highlight the target at point during `embark-act'.") - -(defcustom embark-quit-after-action t - "Should `embark-act' quit the minibuffer? -This controls whether calling `embark-act' without a prefix -argument quits the minibuffer or not. You can always get the -opposite behavior to that indicated by this variable by calling -`embark-act' with \\[universal-argument]. - -Note that `embark-act' can also be called from outside the -minibuffer and this variable is irrelevant in that case. - -In addition to t or nil this variable can also be set to an -alist to specify the minibuffer quitting behavior per command. -In the alist case one can additionally use the key t to -prescribe a default for commands not used as alist keys." - :type '(choice (const :tag "Always quit" t) - (const :tag "Never quit" nil) - (alist :tag "Configure per action" - :key-type (choice (function :tag "Action") - (const :tag "All other actions" t)) - :value-type (choice (const :tag "Quit" t) - (const :tag "Do not quit" nil))))) - -(defcustom embark-confirm-act-all t - "Should `embark-act-all' prompt the user for confirmation? -Even if this variable is nil you may still be prompted to confirm -some uses of `embark-act-all', namely, for those actions whose -entry in `embark-pre-action-hooks' includes `embark--confirm'." - :type 'boolean) - -(defcustom embark-default-action-overrides nil - "Alist associating target types with overriding default actions. -When the source of a target is minibuffer completion, the default -action for it is usually the command that opened the minibuffer -in the first place but this can be overridden for a given type by -an entry in this list. - -For example, if you run `delete-file' the default action for its -completion candidates is `delete-file' itself. You may prefer to -make `find-file' the default action for all files, even if they -were obtained from a `delete-file' prompt. In that case you can -configure that by adding an entry to this variable pairing `file' -with `find-file'. - -In addition to target types, you can also use as keys in this alist, -pairs of a target type and a command name. Such a pair indicates that -the override only applies if the target was obtained from minibuffer -completion from that command. For example adding an -entry (cons (cons \\='file \\='delete-file) \\='find-file) to this alist would -indicate that for files at the prompt of the `delete-file' command, -`find-file' should be used as the default action." - :type '(alist :key-type (choice (symbol :tag "Type") - (cons (symbol :tag "Type") - (symbol :tag "Command"))) - :value-type (function :tag "Default action"))) - -(defcustom embark-target-injection-hooks - '((async-shell-command embark--allow-edit embark--shell-prep) - (shell-command embark--allow-edit embark--shell-prep) - (pp-eval-expression embark--eval-prep) - (eval-expression embark--eval-prep) - (package-delete embark--force-complete) - ;; commands evaluating code found in the buffer, which may in turn prompt - (embark-pp-eval-defun embark--ignore-target) - (eval-defun embark--ignore-target) - (eval-last-sexp embark--ignore-target) - (embark-eval-replace embark--ignore-target) - ;; commands which prompt for something that is *not* the target - (write-region embark--ignore-target) - (append-to-file embark--ignore-target) - (append-to-buffer embark--ignore-target) - (shell-command-on-region embark--ignore-target) - (format-encode-region embark--ignore-target) - (format-decode-region embark--ignore-target) - (xref-find-definitions embark--ignore-target) - (xref-find-references embark--ignore-target) - (sort-regexp-fields embark--ignore-target) - (align-regexp embark--ignore-target)) - "Alist associating commands with post-injection setup hooks. -For commands appearing as keys in this alist, run the -corresponding value as a setup hook after injecting the target -into in the minibuffer and before acting on it. The hooks must -accept arbitrary keyword arguments. The :action command, the -:target string and target :type are always present. For actions -at point the target :bounds are passed too. The default pre-action -hook is specified by the entry with key t. Furthermore, hooks with -the key :always are executed always." - :type '(alist :key-type - (choice symbol - (const :tag "Default" t) - (const :tag "Always" :always)) - :value-type hook)) - -(defcustom embark-pre-action-hooks - `(;; commands that need to position point at the beginning or end - (eval-last-sexp embark--end-of-target) - (indent-pp-sexp embark--beginning-of-target) - (backward-up-list embark--beginning-of-target) - (backward-list embark--beginning-of-target) - (forward-list embark--end-of-target) - (forward-sexp embark--end-of-target) - (backward-sexp embark--beginning-of-target) - (raise-sexp embark--beginning-of-target) - (kill-sexp embark--beginning-of-target) - (mark-sexp embark--beginning-of-target) - (transpose-sexps embark--end-of-target) - (transpose-sentences embark--end-of-target) - (transpose-paragraphs embark--end-of-target) - (forward-sentence embark--end-of-target) - (backward-sentence embark--beginning-of-target) - (backward-paragraph embark--beginning-of-target) - (embark-insert embark--end-of-target) - ;; commands we want to be able to jump back from - ;; (embark-find-definition achieves this by calling - ;; xref-find-definitions which pushes the markers itself) - (find-library embark--xref-push-marker) - ;; commands which prompt the user for confirmation before running - (delete-file embark--confirm) - (delete-directory embark--confirm) - (kill-buffer embark--confirm) - (embark-kill-buffer-and-window embark--confirm) - (bookmark-delete embark--confirm) - (package-delete embark--confirm) - (,'tab-bar-close-tab-by-name embark--confirm) ;; Avoid package-lint warning - ;; search for region contents outside said region - (embark-isearch-forward embark--unmark-target) - (embark-isearch-backward embark--unmark-target) - (occur embark--unmark-target) - (query-replace embark--beginning-of-target embark--unmark-target) - (query-replace-regexp embark--beginning-of-target embark--unmark-target) - (replace-string embark--beginning-of-target embark--unmark-target) - (replace-regexp embark--beginning-of-target embark--unmark-target) - ;; mark pseudo-action - (mark embark--mark-target) - ;; shells in new buffers - (shell embark--universal-argument) - (eshell embark--universal-argument)) - "Alist associating commands with pre-action hooks. -The hooks are run right before an action is embarked upon. See -`embark-target-injection-hooks' for information about the hook -arguments and more details." - :type '(alist :key-type - (choice symbol - (const :tag "Default" t) - (const :tag "Always" :always)) - :value-type hook)) - -(defcustom embark-post-action-hooks - `((bookmark-delete embark--restart) - (bookmark-rename embark--restart) - (delete-file embark--restart) - (embark-kill-ring-remove embark--restart) - (embark-recentf-remove embark--restart) - (embark-history-remove embark--restart) - (rename-file embark--restart) - (copy-file embark--restart) - (delete-directory embark--restart) - (make-directory embark--restart) - (kill-buffer embark--restart) - (embark-rename-buffer embark--restart) - (,'tab-bar-rename-tab-by-name embark--restart) ;; Avoid package-lint warning - (,'tab-bar-close-tab-by-name embark--restart) - (package-delete embark--restart)) - "Alist associating commands with post-action hooks. -The hooks are run after an embarked upon action concludes. See -`embark-target-injection-hooks' for information about the hook -arguments and more details." - :type '(alist :key-type - (choice symbol - (const :tag "Default" t) - (const :tag "Always" :always)) - :value-type hook)) - -(defcustom embark-around-action-hooks - '(;; use directory of target as default-directory - (shell embark--cd) - (eshell embark--cd) - ;; mark the target preserving point and previous mark - (kill-region embark--mark-target) - (kill-ring-save embark--mark-target) - (indent-region embark--mark-target) - (ispell-region embark--mark-target) - (fill-region embark--mark-target) - (upcase-region embark--mark-target) - (downcase-region embark--mark-target) - (capitalize-region embark--mark-target) - (count-words-region embark--mark-target) - (count-words embark--mark-target) - (delete-duplicate-lines embark--mark-target) - (shell-command-on-region embark--mark-target) - (delete-region embark--mark-target) - (format-encode-region embark--mark-target) - (format-decode-region embark--mark-target) - (write-region embark--mark-target) - (append-to-file embark--mark-target) - (append-to-buffer embark--mark-target) - (shell-command-on-region embark--mark-target) - (embark-eval-replace embark--mark-target) - (delete-indentation embark--mark-target) - (comment-dwim embark--mark-target) - (insert-parentheses embark--mark-target) - (insert-pair embark--mark-target) - (org-emphasize embark--mark-target) - ;; do the actual work of selecting & deselecting targets - (embark-select embark--select)) - "Alist associating commands with post-action hooks. -The hooks are run instead of the embarked upon action. The hook -can decide whether or not to run the action or it can run it -in some special environment, like inside a let-binding or inside -`save-excursion'. Each hook is called with keyword argument :run -providing a function encapsulating the following around hooks and -the action; the hook additionally receives the keyword arguments -used for other types of action hooks, for more details see -`embark-target-injection-hooks'." - :type '(alist :key-type - (choice symbol - (const :tag "Default" t) - (const :tag "Always" :always)) - :value-type hook)) - -(when (version-list-< (version-to-list emacs-version) '(29 1)) - ;; narrow to target for duration of action - (setf (alist-get 'repunctuate-sentences embark-around-action-hooks) - '(embark--narrow-to-target))) - -(defcustom embark-multitarget-actions '(embark-insert embark-copy-as-kill) - "Commands for which `embark-act-all' should pass a list of targets. -Normally `embark-act-all' runs the same action on each candidate -separately, but when a command included in this variable's value -is used as an action, `embark-act-all' will instead call it -non-interactively with a single argument: the list of all -candidates. For commands on this list `embark-act' behaves -similarly: it calls them non-interactively with a single -argument: a one element list containing the target." - :type '(repeat function)) - -(defcustom embark-repeat-actions - '((mark . region) - ;; outline commands - outline-next-visible-heading outline-previous-visible-heading - outline-forward-same-level outline-backward-same-level - outline-demote outline-promote - outline-show-subtree (outline-mark-subtree . region) - outline-move-subtree-up outline-move-subtree-down - outline-up-heading outline-hide-subtree outline-cycle - ;; org commands (remapped outline commands) - org-forward-heading-same-level org-backward-heading-same-level - org-next-visible-heading org-previous-visible-heading - org-demote-subtree org-promote-subtree - org-show-subtree (org-mark-subtree . region) - org-move-subtree-up org-move-subtree-down - ;; transpose commands - transpose-sexps transpose-sentences transpose-paragraphs - ;; navigation commands - flymake-goto-next-error flymake-goto-prev-error - embark-next-symbol embark-previous-symbol - backward-up-list backward-list forward-list forward-sexp - backward-sexp forward-sentence backward-sentence - forward-paragraph backward-paragraph - ;; smerge commands - smerge-refine smerge-combine-with-next smerge-prev smerge-next) - "List of repeatable actions. -When you use a command on this list as an Embark action from -outside the minibuffer, `embark-act' does not exit but instead -lets you act again on the possibly new target you reach. - -By default, after using one of these actions, when `embark-act' -looks for targets again, it will start the target cycle at the -same type as the previously acted upon target; that is, you -\"don't loose your place in the target cycle\". - -Sometimes, however, you'll want to prioritize a different type of -target to continue acting on. The main example of this that if -you use a marking command as an action, you almost always want to -act on the region next. For those cases, in addition to -commands, you can also place on this list a pair of a command and -the desired starting type for the target cycle for the next -action." - :type '(repeat (choice function - (cons function - (symbol :tag "Next target type"))))) - -;;; Overlay properties - -;; high priority to override both bug reference and the lazy -;; isearch highlights in embark-isearch-highlight-indicator -(put 'embark-target-overlay 'face 'embark-target) -(put 'embark-target-overlay 'priority 1001) -(put 'embark-selected-overlay 'face 'embark-selected) -(put 'embark-selected-overlay 'priority 1001) - -;;; Stashing information for actions in buffer local variables - -(defvar-local embark--type nil - "Cache for the completion type, meant to be set buffer-locally.") - -(defvar-local embark--target-buffer nil - "Cache for the previous buffer, meant to be set buffer-locally.") - -(defvar-local embark--target-window nil - "Cache for the previous window, meant to be set buffer-locally. -Since windows can be reused to display different buffers, this -window should only be used if it displays the buffer stored in -the variable `embark--target-buffer'.") - -(defvar-local embark--command nil - "Command that started the completion session.") - -(defvar-local embark--toggle-quit nil - "Should we toggle the default quitting behavior for the next action?") - -(defun embark--minibuffer-point () - "Return length of minibuffer contents." - (max 0 (- (point) (minibuffer-prompt-end)))) - -(defun embark--default-directory () - "Guess a reasonable default directory for the current candidates." - (if (and (minibufferp) minibuffer-completing-file-name) - (let ((end (minibuffer-prompt-end)) - (contents (minibuffer-contents))) - (expand-file-name - (substitute-in-file-name - (buffer-substring - end - (+ end - (or (cdr - (last - (completion-all-completions - contents - minibuffer-completion-table - minibuffer-completion-predicate - (embark--minibuffer-point)))) - (cl-position ?/ contents :from-end t) - 0)))))) - default-directory)) - -(defun embark--target-buffer () - "Return buffer that should be targeted by Embark actions." - (cond - ((and (minibufferp) minibuffer-completion-table (minibuffer-selected-window)) - (window-buffer (minibuffer-selected-window))) - ((and embark--target-buffer (buffer-live-p embark--target-buffer)) - embark--target-buffer) - (t (current-buffer)))) - -(defun embark--target-window (&optional display) - "Return window which should be selected when Embark actions run. -If DISPLAY is non-nil, call `display-buffer' to produce the -window if necessary." - (cond - ((and (minibufferp) minibuffer-completion-table (minibuffer-selected-window)) - (minibuffer-selected-window)) - ((and embark--target-window - (window-live-p embark--target-window) - (or (not (buffer-live-p embark--target-buffer)) - (eq (window-buffer embark--target-window) embark--target-buffer))) - embark--target-window) - ((and embark--target-buffer (buffer-live-p embark--target-buffer)) - (or (get-buffer-window embark--target-buffer) - (when display (display-buffer embark--target-buffer)))) - (display (selected-window)))) - -(defun embark--cache-info (buffer) - "Cache information needed for actions in variables local to BUFFER. -BUFFER defaults to the current buffer." - (let ((cmd embark--command) - (dir (embark--default-directory)) - (target-buffer (embark--target-buffer)) - (target-window (embark--target-window))) - (with-current-buffer buffer - (setq embark--command cmd - default-directory dir - embark--target-buffer target-buffer - embark--target-window target-window)))) - -(defun embark--cache-info--completion-list () - "Cache information needed for actions in a *Completions* buffer. -Meant to be be added to `completion-setup-hook'." - ;; when completion-setup-hook hook runs, the *Completions* buffer is - ;; available in the variable standard-output - (embark--cache-info standard-output) - (with-current-buffer standard-output - (when (minibufferp completion-reference-buffer) - (setq embark--type - (completion-metadata-get - (with-current-buffer completion-reference-buffer - (embark--metadata)) - 'category))))) - -;; We have to add this *after* completion-setup-function because that's -;; when the buffer is put in completion-list-mode and turning the mode -;; on kills all local variables! So we use a depth of 5. -(add-hook 'completion-setup-hook #'embark--cache-info--completion-list 5) - -;;;###autoload -(progn - (defun embark--record-this-command () - "Record command which opened the minibuffer. -We record this because it will be the default action. -This function is meant to be added to `minibuffer-setup-hook'." - (setq-local embark--command this-command)) - (add-hook 'minibuffer-setup-hook #'embark--record-this-command)) - -;;; Internal variables - -(defvar embark--prompter-history nil - "History used by the `embark-completing-read-prompter'.") - -;;; Core functionality - -(defconst embark--verbose-indicator-buffer " *Embark Actions*") - -(defvar embark--minimal-indicator-overlay nil) - -(defun embark--metadata () - "Return current minibuffer completion metadata." - (completion-metadata - (buffer-substring-no-properties - (minibuffer-prompt-end) - (max (minibuffer-prompt-end) (point))) - minibuffer-completion-table - minibuffer-completion-predicate)) - -(defun embark-target-active-region () - "Target the region if active." - (when (use-region-p) - (let ((start (region-beginning)) - (end (region-end))) - `(region ,(buffer-substring start end) . (,start . ,end))))) - -(autoload 'dired-get-filename "dired") -(declare-function image-dired-original-file-name "image-dired") - -(defun embark-target-guess-file-at-point () - "Target the file guessed by `ffap' at point." - (when-let ((tap-file (thing-at-point 'filename)) - ((not (ffap-url-p tap-file))) ; no URLs, those have a target finder - (bounds (bounds-of-thing-at-point 'filename)) - (file (ffap-file-at-point))) - ;; ffap doesn't make bounds available, so we use - ;; thingatpt bounds, which might be a little off - ;; adjust bounds if thingatpt gobbled punctuation around file - (when (or (string-match (regexp-quote file) tap-file) - (string-match (regexp-quote (file-name-base file)) tap-file)) - (setq bounds (cons (+ (car bounds) (match-beginning 0)) - (- (cdr bounds) (- (length tap-file) - (match-end 0)))))) - `(file ,(abbreviate-file-name (expand-file-name file)) ,@bounds))) - -(defun embark-target-file-at-point () - "Target file at point. -This function mostly relies on `ffap-file-at-point', with the -following exceptions: - -- In `dired-mode', it uses `dired-get-filename' instead. - -- In `imaged-dired-thumbnail-mode', it uses - `image-dired-original-file-name' instead." - (let (file bounds) - (or (and (derived-mode-p 'dired-mode) - (setq file (dired-get-filename t 'no-error-if-not-filep)) - (setq bounds - (cons - (save-excursion (dired-move-to-filename) (point)) - (save-excursion (dired-move-to-end-of-filename) (point))))) - (and (derived-mode-p 'image-dired-thumbnail-mode) - (setq file (image-dired-original-file-name)) - (setq bounds (cons (point) (1+ (point))))) - (when-let ((tap-file (thing-at-point 'filename)) - ((not (equal (file-name-base tap-file) tap-file))) - (guess (embark-target-guess-file-at-point))) - (setq file (cadr guess) bounds (cddr guess)))) - (when file - `(file ,(abbreviate-file-name (expand-file-name file)) ,@bounds)))) - -(defun embark-target-package-at-point () - "Target the package on the current line in a packages buffer." - (when (derived-mode-p 'package-menu-mode) - (when-let ((pkg (get-text-property (point) 'tabulated-list-id))) - `(package ,(symbol-name (package-desc-name pkg)) - ,(line-beginning-position) . ,(line-end-position))))) - -(defun embark-target-email-at-point () - "Target the email address at point." - (when-let ((email (thing-at-point 'email))) - (when (string-prefix-p "mailto:" email) - (setq email (string-remove-prefix "mailto:" email))) - `(email ,email . ,(bounds-of-thing-at-point 'email)))) - -(defun embark-target-url-at-point () - "Target the URL at point." - (if-let ((url (or (get-text-property (point) 'shr-url) - (get-text-property (point) 'image-url)))) - `(url ,url - ,(previous-single-property-change - (min (1+ (point)) (point-max)) 'mouse-face nil (point-min)) - . ,(next-single-property-change - (point) 'mouse-face nil (point-max))) - (when-let ((url (thing-at-point 'url))) - `(url ,url . ,(thing-at-point-bounds-of-url-at-point t))))) - -(declare-function widget-at "wid-edit") - -(defun embark-target-custom-variable-at-point () - "Target the variable corresponding to the customize widget at point." - (when (derived-mode-p 'Custom-mode) - (save-excursion - (beginning-of-line) - (when-let* ((widget (widget-at (point))) - (var (and (eq (car widget) 'custom-visibility) - (plist-get (cdr widget) :parent))) - (sym (and (eq (car var) 'custom-variable) - (plist-get (cdr var) :value)))) - `(variable - ,(symbol-name sym) - ,(point) - . ,(progn - (re-search-forward ":" (line-end-position) 'noerror) - (point))))))) - -;; NOTE: There is also (thing-at-point 'list), however it does -;; not work on strings and requires the point to be inside the -;; parentheses. This version here is slightly more general. -(defun embark-target-expression-at-point () - "Target expression at point." - (cl-flet ((syntax-p (class &optional (delta 0)) - (and (<= (point-min) (+ (point) delta) (point-max)) - (eq (pcase class - ('open 4) ('close 5) ('prefix 6) ('string 7)) - (syntax-class (syntax-after (+ (point) delta))))))) - (when-let - ((start - (pcase-let ((`(_ ,open _ ,string _ _ _ _ ,start _ _) (syntax-ppss))) - (ignore-errors ; set start=nil if delimiters are unbalanced - (cond - (string start) - ((or (syntax-p 'open) (syntax-p 'prefix)) - (save-excursion (backward-prefix-chars) (point))) - ((syntax-p 'close -1) - (save-excursion - (backward-sexp) (backward-prefix-chars) (point))) - ((syntax-p 'string) (point)) - ((syntax-p 'string -1) (scan-sexps (point) -1)) - (t open))))) - (end (ignore-errors (scan-sexps start 1)))) - (unless (eq start (car (bounds-of-thing-at-point 'defun))) - `(expression ,(buffer-substring start end) ,start . ,end))))) - -(defmacro embark-define-overlay-target (name prop &optional pred type target) - "Define a target finder for NAME that targets overlays with property PROP. -The function defined is named embark-target-NAME-at-point and it -returns Embark targets based on the overlays around point. An -overlay provides a target if its property named PROP is non-nil. - -If the optional PRED argument is given, it should be an -expression and it further restricts the targets to only those -overlays for which PRED evaluates to non-nil. - -The target finder returns target type NAME or optional symbol -TYPE if given. - -The target finder returns the substring of the buffer covered by -the overlay as the target string or the result of evaluating the -optional TARGET expression if given. - -PRED and TARGET are expressions (not functions) and when evaluated the -symbols `%o' and `%p' are bound to the overlay and the overlay's -property respectively." - `(defun ,(intern (format "embark-target-%s-at-point" name)) () - ,(format "Target %s at point." name) - (when-let ((%o (seq-find - (lambda (%o) - (when-let ((%p (overlay-get %o ',prop))) - (ignore %p) - ,(or pred t))) - (overlays-in (max (point-min) (1- (point))) - (min (point-max) (1+ (point)))))) - (%p (overlay-get %o ',prop))) - (ignore %p) - (cons ',(or type name) - (cons ,(or target `(buffer-substring-no-properties - (overlay-start %o) (overlay-end %o))) - (cons (overlay-start %o) (overlay-end %o))))))) - -(embark-define-overlay-target flymake flymake-diagnostic) -(embark-define-overlay-target bug-reference bug-reference-url nil url %p) -(embark-define-overlay-target smerge smerge (eq %p 'conflict)) - -(defmacro embark-define-thingatpt-target (thing &rest modes) - "Define a target finder for THING using the thingatpt library. -The function defined is named embark-target-NAME-at-point and it -uses (thing-at-point 'THING) to find its targets. - -If any MODES are given, the target finder only applies to buffers -in one of those major modes." - (declare (indent 1)) - `(defun ,(intern (format "embark-target-%s-at-point" thing)) () - ,(format "Target %s at point." thing) - (when ,(if modes `(derived-mode-p ,@(mapcar (lambda (m) `',m) modes)) t) - (when-let (bounds (bounds-of-thing-at-point ',thing)) - (cons ',thing (cons - (buffer-substring (car bounds) (cdr bounds)) - bounds)))))) - -(embark-define-thingatpt-target defun) -(embark-define-thingatpt-target sentence - text-mode help-mode Info-mode man-common) -(embark-define-thingatpt-target paragraph - text-mode help-mode Info-mode man-common) - -(defmacro embark-define-regexp-target - (name regexp &optional type target bounds limit) - "Define a target finder for matches of REGEXP around point. -The function defined is named embark-target-NAME-at-point and it -uses (thing-at-point-looking-at REGEXP) to find its targets. - -The target finder returns target type NAME or optional symbol -TYPE if given. - -The target finder returns the substring of the buffer matched by -REGEXP as the target string or the result of evaluating the -optional TARGET expression if given. In the expression TARGET -you can use `match-string' to recover the match of the REGEXP or -of any sub-expressions it has. - -BOUNDS is an optional expression to compute the bounds of the -target and defaults to (cons (match-beginning 0) (match-end 0)). - -The optional LIMIT is the number of characters before and after -point to limit the search to. If LIMIT is nil, search a little -more than the current line (more precisely, the smallest interval -centered at point that includes the current line)." - `(defun ,(intern (format "embark-target-%s-at-point" name)) () - ,(format "Target %s at point." name) - (save-match-data - (when (thing-at-point-looking-at - ,regexp - ,(or limit '(max (- (pos-eol) (point)) (- (point) (pos-bol))))) - (cons ',(or type name) - (cons ,(or target '(match-string 0)) - ,(or bounds - '(cons (match-beginning 0) (match-end 0))))))))) - -(defun embark--identifier-types (identifier) - "Return list of target types appropriate for IDENTIFIER." - (let ((symbol (intern-soft identifier))) - (if (not - (or (derived-mode-p 'emacs-lisp-mode 'inferior-emacs-lisp-mode) - (and (not (derived-mode-p 'prog-mode)) - symbol - (or (boundp symbol) (fboundp symbol) (symbol-plist symbol))))) - '(identifier) - (let* ((library (ffap-el-mode identifier)) - (types - (append - (and (commandp symbol) '(command)) - (and symbol (boundp symbol) (not (keywordp symbol)) '(variable)) - (and (fboundp symbol) (not (commandp symbol)) '(function)) - (and (facep symbol) '(face)) - (and library '(library)) - (and (featurep 'package) (embark--package-desc symbol) - '(package))))) - (when (and library - (looking-back "\\(?:require\\|use-package\\).*" - (line-beginning-position))) - (setq types (embark--rotate types (cl-position 'library types)))) - (or types '(symbol)))))) - -(defun embark-target-identifier-at-point () - "Target identifier at point. - -In Emacs Lisp and IELM buffers the identifier is promoted to a -symbol, for which more actions are available. Identifiers are -also promoted to symbols if they are interned Emacs Lisp symbols -and found in a buffer in a major mode that is not derived from -`prog-mode' (this is intended for when you might be reading or -writing about Emacs). - -As a convenience, in Org Mode an initial ' or surrounding == or -~~ are removed." - (when-let (bounds (bounds-of-thing-at-point 'symbol)) - (let ((name (buffer-substring (car bounds) (cdr bounds)))) - (when (derived-mode-p 'org-mode) - (cond ((string-prefix-p "'" name) - (setq name (substring name 1)) - (cl-incf (car bounds))) - ((string-match-p "^\\([=~]\\).*\\1$" name) - (setq name (substring name 1 -1)) - (cl-incf (car bounds)) - (cl-decf (cdr bounds))))) - (mapcar (lambda (type) `(,type ,name . ,bounds)) - (embark--identifier-types name))))) - -(defun embark-target-heading-at-point () - "Target the outline heading at point." - (let ((beg (line-beginning-position)) - (end (line-end-position))) - (when (save-excursion - (goto-char beg) - (and (bolp) - (looking-at - ;; default definition from outline.el - (or (bound-and-true-p outline-regexp) "[*\^L]+")))) - (require 'outline) ;; Ensure that outline commands are available - `(heading ,(buffer-substring beg end) ,beg . ,end)))) - -(defun embark-target-text-heading-at-point () - "Target the outline heading at point in text modes." - (when (derived-mode-p 'text-mode) - (embark-target-heading-at-point))) - -(defun embark-target-prog-heading-at-point () - "Target the outline heading at point in programming modes." - (when (derived-mode-p 'prog-mode) - (embark-target-heading-at-point))) - -(defun embark-target-top-minibuffer-candidate () - "Target the top completion candidate in the minibuffer. -Return the category metadatum as the type of the target. - -This target finder is meant for the default completion UI and -completion UI highly compatible with it, like Icomplete. -Many completion UIs can still work with Embark but will need -their own target finder. See for example -`embark--vertico-selected'." - (when (and (minibufferp) minibuffer-completion-table) - (pcase-let* ((`(,category . ,candidates) (embark-minibuffer-candidates)) - (contents (minibuffer-contents)) - (top (if (test-completion contents - minibuffer-completion-table - minibuffer-completion-predicate) - contents - (let ((completions (completion-all-sorted-completions))) - (if (null completions) - contents - (concat - (substring contents - 0 (or (cdr (last completions)) 0)) - (car completions))))))) - (cons category (or (car (member top candidates)) top))))) - -(defun embark-target-collect-candidate () - "Target the collect candidate at point." - (when (derived-mode-p 'embark-collect-mode) - (when-let ((button - (pcase (get-text-property (point) 'tabulated-list-column-name) - ("Candidate" (button-at (point))) - ("Annotation" (previous-button (point))))) - (start (button-start button)) - (end (button-end button)) - (candidate (tabulated-list-get-id))) - `(,embark--type - ,(if (eq embark--type 'file) - (abbreviate-file-name (expand-file-name candidate)) - candidate) - ,start . ,end)))) - -(defun embark-target-completion-list-candidate () - "Return the completion candidate at point in a completions buffer." - (when (derived-mode-p 'completion-list-mode) - (if (not (get-text-property (point) 'mouse-face)) - (user-error "No completion here") - ;; this fairly delicate logic is taken from `choose-completion' - (let (beg end) - (cond - ((and (not (eobp)) (get-text-property (point) 'mouse-face)) - (setq end (point) beg (1+ (point)))) - ((and (not (bobp)) - (get-text-property (1- (point)) 'mouse-face)) - (setq end (1- (point)) beg (point))) - (t (user-error "No completion here"))) - (setq beg (previous-single-property-change beg 'mouse-face)) - (setq end (or (next-single-property-change end 'mouse-face) - (point-max))) - (let ((raw (or (get-text-property beg 'completion--string) - (buffer-substring beg end)))) - `(,embark--type - ,(if (eq embark--type 'file) - (abbreviate-file-name (expand-file-name raw)) - raw) - ,beg . ,end)))))) - -(defun embark--cycle-key () - "Return the key to use for `embark-cycle'." - (if embark-cycle-key - (if (key-valid-p embark-cycle-key) - (key-parse embark-cycle-key) - (error "`embark-cycle-key' is invalid")) - (car (where-is-internal #'embark-act)))) - -(defun embark--raw-action-keymap (type) - "Return raw action map for targets of given TYPE. -This does not take into account the default action, help key or -cycling bindings, just what's registered in -`embark-keymap-alist'." - (make-composed-keymap - (mapcar #'symbol-value - (let ((actions (or (alist-get type embark-keymap-alist) - (alist-get t embark-keymap-alist)))) - (ensure-list actions))))) - -(defun embark--action-keymap (type cycle) - "Return action keymap for targets of given TYPE. -If CYCLE is non-nil bind `embark-cycle'." - (make-composed-keymap - (let ((map (make-sparse-keymap)) - (default-action (embark--default-action type))) - (define-key map [13] default-action) - (when-let ((cycle-key (and cycle (embark--cycle-key)))) - (define-key map cycle-key #'embark-cycle)) - (when embark-help-key - (keymap-set map embark-help-key #'embark-keymap-help)) - map) - (embark--raw-action-keymap type))) - -(defun embark--truncate-target (target) - "Truncate TARGET string." - (unless (stringp target) - (setq target (format "%s" target))) - (if-let (pos (string-match-p "\n" target)) - (concat (car (split-string target "\n" 'omit-nulls "\\s-*")) "…") - target)) - -;;;###autoload -(defun embark-eldoc-first-target (report &rest _) - "Eldoc function reporting the first Embark target at point. -This function uses the eldoc REPORT callback and is meant to be -added to `eldoc-documentation-functions'." - (when-let (((not (minibufferp))) - (target (car (embark--targets)))) - (funcall report - (format "Embark on %s ‘%s’" - (plist-get target :type) - (embark--truncate-target (plist-get target :target)))))) - -;;;###autoload -(defun embark-eldoc-target-types (report &rest _) - "Eldoc function reporting the types of all Embark targets at point. -This function uses the eldoc REPORT callback and is meant to be -added to `eldoc-documentation-functions'." - (when-let (((not (minibufferp))) - (targets (embark--targets))) - (funcall report - (format "Embark target types: %s" - (mapconcat - (lambda (target) (symbol-name (plist-get target :type))) - targets - ", "))))) - -(defun embark--format-targets (target shadowed-targets rep) - "Return a formatted string indicating the TARGET of an action. - -This is used internally by the minimal indicator and for the -targets section of the verbose indicator. The string will also -mention any SHADOWED-TARGETS. A non-nil REP indicates we are in -a repeating sequence of actions." - (let ((act (propertize - (cond - ((plist-get target :multi) "∀ct") - (rep "Rep") - (t "Act")) - 'face 'highlight))) - (cond - ((eq (plist-get target :type) 'embark-become) - (propertize "Become" 'face 'highlight)) - ((and (minibufferp) - (not (eq 'embark-keybinding - (completion-metadata-get - (embark--metadata) - 'category)))) - ;; we are in a minibuffer but not from the - ;; completing-read prompter, use just "Act" - act) - ((plist-get target :multi) - (format "%s on %s %ss" - act - (plist-get target :multi) - (plist-get target :type))) - (t (format - "%s on %s%s ‘%s’" - act - (plist-get target :type) - (if shadowed-targets - (format (propertize "(%s)" 'face 'shadow) - (mapconcat - (lambda (target) (symbol-name (plist-get target :type))) - shadowed-targets - ", ")) - "") - (embark--truncate-target (plist-get target :target))))))) - -(defun embark-minimal-indicator () - "Minimal indicator, appearing in the minibuffer prompt or echo area. -This indicator displays a message showing the types of all -targets, starting with the current target, and the value of the -current target. The message is displayed in the echo area, or if -the minibuffer is open, the message is added to the prompt." - (lambda (&optional keymap targets _prefix) - (if (null keymap) - (when embark--minimal-indicator-overlay - (delete-overlay embark--minimal-indicator-overlay) - (setq-local embark--minimal-indicator-overlay nil)) - (let ((indicator (embark--format-targets - (car targets) (cdr targets) - (eq (lookup-key keymap [13]) #'embark-done)))) - (if (not (minibufferp)) - (message "%s" indicator) - (unless embark--minimal-indicator-overlay - (setq-local embark--minimal-indicator-overlay - (make-overlay (point-min) (point-min) - (current-buffer) t t))) - (overlay-put embark--minimal-indicator-overlay - 'before-string (concat indicator - (if (<= (length indicator) - (* 0.4 (frame-width))) - " " - "\n")))))))) - -(defun embark--read-key-sequence (update) - "Read key sequence, call UPDATE function with prefix keys." - (let (timer prefix) - (unwind-protect - (progn - (when (functionp update) - (setq timer (run-at-time - 0.05 0.05 - (lambda () - (let ((new-prefix (this-single-command-keys))) - (unless (equal prefix new-prefix) - (setq prefix new-prefix) - (when (/= (length prefix) 0) - (funcall update prefix)))))))) - (read-key-sequence-vector nil nil nil t 'cmd-loop)) - (when timer - (cancel-timer timer))))) - -(defvar embark-indicators) ; forward declaration - -(defun embark-keymap-prompter (keymap update) - "Let the user choose an action using the bindings in KEYMAP. -Besides the bindings in KEYMAP, the user is free to use all their -key bindings and even \\[execute-extended-command] to select a command. -UPDATE is the indicator update function." - (let* ((keys (let ((overriding-terminal-local-map keymap)) - (embark--read-key-sequence update))) - (cmd (let ((overriding-terminal-local-map keymap)) - (key-binding keys 'accept-default)))) - ;; Set last-command-event as it would be from the command loop. - ;; Previously we only set it locally for digit-argument and for - ;; the mouse scroll commands handled in this function. But other - ;; commands can need it too! For example, electric-pair-mode users - ;; may wish to bind ( to self-insert-command in embark-region-map. - ;; Also, as described in issue #402, there are circumstances where - ;; you might run consult-narrow through the embark-keymap-prompter. - (setq last-command-event (aref keys (1- (length keys)))) - (pcase cmd - ((or 'embark-keymap-help - (and 'nil ; cmd is nil but last key is help-char - (guard (eq help-char (aref keys (1- (length keys))))))) - (let ((embark-indicators - (cl-set-difference embark-indicators - '(embark-verbose-indicator - embark-mixed-indicator))) - (prefix-map - (if (eq cmd 'embark-keymap-help) - keymap - (let ((overriding-terminal-local-map keymap)) - (key-binding (seq-take keys (1- (length keys))) - 'accept-default)))) - (prefix-arg prefix-arg)) ; preserve prefix arg - (when-let ((win (get-buffer-window embark--verbose-indicator-buffer - 'visible))) - (quit-window 'kill-buffer win)) - (embark-completing-read-prompter prefix-map update))) - ((or 'universal-argument 'universal-argument-more - 'negative-argument 'digit-argument 'embark-toggle-quit) - ;; prevent `digit-argument' from modifying the overriding map - (let ((overriding-terminal-local-map overriding-terminal-local-map)) - (command-execute cmd)) - (embark-keymap-prompter - (make-composed-keymap universal-argument-map keymap) - update)) - ((or 'minibuffer-keyboard-quit 'abort-recursive-edit 'abort-minibuffers) - nil) - ((guard (let ((def (lookup-key keymap keys))) ; if directly - ; bound, then obey - (and def (not (numberp def))))) ; number means "invalid prefix" - cmd) - ((and (pred symbolp) - (guard (string-suffix-p "self-insert-command" (symbol-name cmd)))) - (minibuffer-message "Not an action") - (embark-keymap-prompter keymap update)) - ((or 'scroll-other-window 'scroll-other-window-down) - (let ((minibuffer-scroll-window - ;; NOTE: Here we special case the verbose indicator! - (or (get-buffer-window embark--verbose-indicator-buffer 'visible) - minibuffer-scroll-window))) - (ignore-errors (command-execute cmd))) - (embark-keymap-prompter keymap update)) - ((or 'scroll-bar-toolkit-scroll 'mwheel-scroll - 'mac-mwheel-scroll 'pixel-scroll-precision) - (funcall cmd last-command-event) - (embark-keymap-prompter keymap update)) - ('execute-extended-command - (let ((prefix-arg prefix-arg)) ; preserve prefix arg - (intern-soft (read-extended-command)))) - ((or 'keyboard-quit 'keyboard-escape-quit) - nil) - (_ cmd)))) - -(defun embark--command-name (cmd) - "Return an appropriate name for CMD. -If CMD is a symbol, use its symbol name; for lambdas, use the -first line of the documentation string; for keyboard macros use -`key-description'; otherwise use the word \"unnamed\"." - (concat ; fresh copy, so we can freely add text properties - (cond - ((or (stringp cmd) (vectorp cmd)) (key-description cmd)) - ((stringp (car-safe cmd)) (car cmd)) - ((eq (car-safe cmd) 'menu-item) (eval (cadr cmd))) - ((keymapp cmd) - (propertize (if (symbolp cmd) (format "+%s" cmd) "<keymap>") - 'face 'embark-keymap)) - ((symbolp cmd) - (let ((name (symbol-name cmd))) - (if (string-prefix-p "embark-action--" name) ; direct action mode - (format "(%s)" (string-remove-prefix "embark-action--" name)) - name))) - ((when-let (doc (and (functionp cmd) (ignore-errors (documentation cmd)))) - (save-match-data - (when (string-match "^\\(.*\\)$" doc) - (match-string 1 doc))))) - (t "<unnamed>")))) - -;; Taken from Marginalia, needed by the verbose indicator. -;; We cannot use the completion annotators in this case. -(defconst embark--advice-regexp - (rx bos - (1+ (seq (? "This function has ") - (or ":before" ":after" ":around" ":override" - ":before-while" ":before-until" ":after-while" - ":after-until" ":filter-args" ":filter-return") - " advice: " (0+ nonl) "\n")) - "\n") - "Regexp to match lines about advice in function documentation strings.") - -;; Taken from Marginalia, needed by the verbose indicator. -;; We cannot use the completion annotators in this case. -(defun embark--function-doc (sym) - "Documentation string of function SYM." - (let ((vstr (and (symbolp sym) (keymapp sym) (boundp sym) - (eq (symbol-function sym) (symbol-value sym)) - (documentation-property sym 'variable-documentation)))) - (when-let (str (or (ignore-errors (documentation sym)) vstr)) - ;; Replace standard description with variable documentation - (when (and vstr (string-match-p "\\`Prefix command" str)) - (setq str vstr)) - (save-match-data - (if (string-match embark--advice-regexp str) - (substring str (match-end 0)) - str))))) - -(defun embark--action-repeatable-p (action) - "Is ACTION repeatable? -When the return value is non-nil it will be the desired starting -point of the next target cycle or t to indicate the default, -namely that the target cycle for the next action should begin at -the type of the current target." - (or (cdr (assq action embark-repeat-actions)) - (and (memq action embark-repeat-actions) t))) - -(defun embark--formatted-bindings (keymap &optional nested) - "Return the formatted keybinding of KEYMAP. -The keybindings are returned in their order of appearance. -If NESTED is non-nil subkeymaps are not flattened." - (let* ((commands - (cl-loop for (key . def) in (embark--all-bindings keymap nested) - for name = (embark--command-name def) - for cmd = (keymap--menu-item-binding def) - unless (memq cmd '(nil embark-keymap-help - negative-argument digit-argument)) - collect (list name cmd key - (concat - (if (eq (car-safe def) 'menu-item) - "menu-item" - (key-description key)))))) - (width (cl-loop for (_name _cmd _key desc) in commands - maximize (length desc))) - (default) - (candidates - (cl-loop for item in commands - for (name cmd key desc) = item - for desc-rep = - (concat - (propertize desc 'face 'embark-keybinding) - (and (embark--action-repeatable-p cmd) - embark-keybinding-repeat)) - for formatted = - (propertize - (concat desc-rep - (make-string (- width (length desc-rep) -1) ?\s) - name) - 'embark-command cmd) - when (equal key [13]) - do (setq default formatted) - collect (cons formatted item)))) - (cons candidates default))) - -(defun embark--with-category (category candidates) - "Return completion table for CANDIDATES of CATEGORY with sorting disabled." - (lambda (string predicate action) - (if (eq action 'metadata) - `(metadata (display-sort-function . identity) - (cycle-sort-function . identity) - (category . ,category)) - (complete-with-action - action candidates string predicate)))) - -(defun embark-completing-read-prompter (keymap update &optional no-default) - "Prompt via completion for a command bound in KEYMAP. -If NO-DEFAULT is t, no default value is passed to`completing-read'. - -UPDATE is the indicator update function. It is not used directly -here, but if the user switches to `embark-keymap-prompter', the -UPDATE function is passed to it." - (let* ((candidates+def (embark--formatted-bindings keymap)) - (candidates (car candidates+def)) - (def (and (not no-default) (cdr candidates+def))) - (buf (current-buffer)) - (choice - (catch 'choice - (minibuffer-with-setup-hook - (lambda () - (let ((map (make-sparse-keymap))) - (define-key map "\M-q" - (lambda () - (interactive) - (with-current-buffer buf - (embark-toggle-quit)))) - (when-let (cycle (embark--cycle-key)) - ;; Rebind `embark-cycle' in order allow cycling - ;; from the `completing-read' prompter. Additionally - ;; `embark-cycle' can be selected via - ;; `completing-read'. The downside is that this breaks - ;; recursively acting on the candidates of type - ;; embark-keybinding in the `completing-read' prompter. - (define-key map cycle - (cond - ((eq (lookup-key keymap cycle) 'embark-cycle) - (lambda () - (interactive) - (throw 'choice 'embark-cycle))) - ((null embark-cycle-key) - (lambda () - (interactive) - (minibuffer-message - "No cycling possible; press `%s' again to act." - (key-description cycle)) - (define-key map cycle #'embark-act)))))) - (when embark-keymap-prompter-key - (keymap-set map embark-keymap-prompter-key - (lambda () - (interactive) - (message "Press key binding") - (let ((cmd (embark-keymap-prompter keymap update))) - (if (null cmd) - (user-error "Unknown key") - (throw 'choice cmd)))))) - (use-local-map - (make-composed-keymap map (current-local-map))))) - (completing-read - "Command: " - (embark--with-category 'embark-keybinding candidates) - nil nil nil 'embark--prompter-history def))))) - (pcase (assoc choice candidates) - (`(,_formatted ,_name ,cmd ,key ,_desc) - ;; Set last-command-event as it would be from the command loop. - (setq last-command-event (aref key (1- (length key)))) - cmd) - ('nil (intern-soft choice))))) - -;;; Verbose action indicator - -(defgroup embark-indicators nil - "Indicators display information about actions and targets." - :group 'embark) - -(defcustom embark-indicators - '(embark-mixed-indicator - embark-highlight-indicator - embark-isearch-highlight-indicator) - "Indicator functions to use when acting or becoming. -The indicator functions are called from both `embark-act' and -from `embark-become' and should display information about this to -the user, such as: which of those two commands is running; a -description of the key bindings that are available for actions or -commands to become; and, in the case of `embark-act', the type -and value of the targets, and whether other targets are available -via `embark-cycle'. The indicator function is free to display as -much or as little of this information as desired and can use any -Emacs interface elements to do so. - -Embark comes with five such indicators: - -- `embark-minimal-indicator', which does not display any - information about keybindings, but does display types and - values of action targets in the echo area or minibuffer prompt, - -- `embark-verbose-indicator', which pops up a buffer containing - detailed information including key bindings and the first line - of the docstring of the commands they run, and - -- `embark-mixed-indicator', which combines the minimal and the - verbose indicator: the minimal indicator is shown first and the - verbose popup is shown after `embark-mixed-indicator-delay' - seconds. - -- `embark-highlight-indicator', which highlights the target - at point. - -- `embark-isearch-highlight-indicator', which when the target at - point is an identifier or symbol, lazily highlights all - occurrences of it. - -The protocol for indicator functions is as follows: - -When called from `embark-act', an indicator function is called -without arguments. The indicator function should then return a -closure, which captures the indicator state. The returned -closure must accept up to three optional arguments, the action -keymap, the targets (plists as returned by `embark--targets') and -the prefix keys typed by the user so far. The keymap, targets -and prefix keys may be updated when cycling targets at point -resulting in multiple calls to the closure. When called from -`embark-become', the indicator closure will be called with the -keymap of commands to become, a fake target list containing a -single target of type `embark-become' and whose value is the -minibuffer input, and the prefix set to nil. Note, in -particular, that if an indicator function wishes to distinguish -between `embark-act' and `embark-become' it should check whether -the `car' of the first target is `embark-become'. - -After the action has been performed the indicator closure is -called without arguments, such that the indicator can perform the -necessary cleanup work. For example, if the indicator adds -overlays, it should remove these overlays. The indicator should -be written in a way that it is safe to call it for cleanup more -than once, in fact, it should be able to handle any sequence of -update and cleanup calls ending in a call for cleanup. - -NOTE: Experience shows that the indicator calling convention may -change again in order to support more action features. The -calling convention should currently be considered unstable. -Please keep this in mind when writing a custom indicator -function, or when using the `which-key' indicator function from -the wiki." - :type '(repeat - (choice - (const :tag "Verbose indicator" embark-verbose-indicator) - (const :tag "Minimal indicator" embark-minimal-indicator) - (const :tag "Mixed indicator" embark-mixed-indicator) - (const :tag "Highlight target" embark-highlight-indicator) - (const :tag "Highlight all occurrences" - embark-isearch-highlight-indicator) - (function :tag "Other")))) - -(defface embark-verbose-indicator-documentation - '((t :inherit completions-annotations)) - "Face used by the verbose action indicator to display binding descriptions. -Used by `embark-verbose-indicator'.") - -(defface embark-verbose-indicator-title '((t :height 1.1 :weight bold)) - "Face used by the verbose action indicator for the title. -Used by `embark-verbose-indicator'.") - -(defface embark-verbose-indicator-shadowed '((t :inherit shadow)) - "Face used by the verbose action indicator for the shadowed targets. -Used by `embark-verbose-indicator'.") - -(defcustom embark-verbose-indicator-display-action - '(display-buffer-reuse-window) - "Parameters added to `display-buffer-alist' to show the actions buffer. -See the docstring of `display-buffer' for information on what -display actions and parameters are available." - :type `(choice - (const :tag "Reuse some window" - (display-buffer-reuse-window)) - (const :tag "Below target buffer" - (display-buffer-below-selected - (window-height . fit-window-to-buffer))) - (const :tag "Bottom of frame (fixed-size)" - (display-buffer-at-bottom)) - (const :tag "Bottom of frame (resizes during cycling)" - (display-buffer-at-bottom - (window-height . fit-window-to-buffer))) - (const :tag "Side window on the right" - (display-buffer-in-side-window (side . right))) - (const :tag "Side window on the left" - (display-buffer-in-side-window (side . left))) - (sexp :tag "Other"))) - -(defcustom embark-verbose-indicator-excluded-actions nil - "Commands not displayed by `embark-verbose-indicator'. -This variable should be set to a list of symbols and regexps. -The verbose indicator will exclude from its listing any commands -matching an element of this list." - :type '(choice - (const :tag "Exclude nothing" nil) - (const :tag "Exclude Embark general actions" - (embark-collect embark-live embark-export - embark-cycle embark-act-all embark-keymap-help - embark-become embark-isearch-forward - embark-isearch-backward)) - (repeat :tag "Other" (choice regexp symbol)))) - -(defcustom embark-verbose-indicator-buffer-sections - `(target "\n" shadowed-targets " " cycle "\n" bindings) - "List of sections to display in the verbose indicator buffer, in order. -You can use either a symbol designating a concrete section (one -of the keywords below, but without the colon), a string literal -or a function returning a string or list of strings to insert and -that accepts the following keyword arguments: - -- `:target', the target as a cons of type and value, -- `:shadowed-targets', a list of conses for the other targets, -- `:bindings' a list returned by `embark--formatted-bindings', and -- `:cycle', a string describing the key binding of `embark-cycle'." - :type '(repeat - (choice (const :tag "Current target name" target) - (const :tag "List of other shadowed targets" shadowed-targets) - (const :tag "Key bindings" bindings) - (const :tag "Cycle indicator" cycle) - (string :tag "Literal string") - (function :tag "Custom function")))) - -(defcustom embark-verbose-indicator-nested t - "Whether the verbose indicator should use nested keymap navigation. -When this variable is non-nil the actions buffer displayed by -`embark-verbose-indicator' will include any prefix keys found in -the keymap it is displaying, and will update to show what is -bound under the prefix if the prefix is pressed. If this -variable is nil, then the actions buffer will contain a flat list -of all full key sequences bound in the keymap." - :type 'boolean) - -(defun embark--verbose-indicator-excluded-p (cmd) - "Return non-nil if CMD should be excluded from the verbose indicator." - (seq-find (lambda (x) - (if (symbolp x) - (eq cmd x) - (string-match-p x (symbol-name cmd)))) - embark-verbose-indicator-excluded-actions)) - -(cl-defun embark--verbose-indicator-section-target - (&key targets bindings &allow-other-keys) - "Format the TARGETS section for the indicator buffer. -BINDINGS is the formatted list of keybindings." - (let ((result (embark--format-targets - (car targets) - nil ; the shadowed targets section deals with these - (cl-find 'embark-done bindings :key #'caddr :test #'eq)))) - (add-face-text-property 0 (length result) - 'embark-verbose-indicator-title - 'append - result) - result)) - -(cl-defun embark--verbose-indicator-section-cycle - (&key cycle shadowed-targets &allow-other-keys) - "Format the CYCLE key section for the indicator buffer. -SHADOWED-TARGETS is the list of other targets." - (concat - (and cycle (propertize (format "(%s to cycle)" cycle) - 'face 'embark-verbose-indicator-shadowed)) - (and shadowed-targets "\n"))) - -(cl-defun embark--verbose-indicator-section-shadowed-targets - (&key shadowed-targets &allow-other-keys) - "Format the SHADOWED-TARGETS section for the indicator buffer." - (when shadowed-targets - (propertize (format "Shadowed targets at point: %s" - (string-join shadowed-targets ", ")) - 'face 'embark-verbose-indicator-shadowed))) - -(cl-defun embark--verbose-indicator-section-bindings - (&key bindings &allow-other-keys) - "Format the BINDINGS section for the indicator buffer." - (let* ((max-width (apply #'max (cons 0 (mapcar (lambda (x) - (string-width (car x))) - bindings)))) - (fmt (format "%%-%ds" (1+ max-width))) - (result nil)) - (dolist (binding bindings (string-join (nreverse result))) - (let ((cmd (caddr binding))) - (unless (embark--verbose-indicator-excluded-p cmd) - (let ((keys (format fmt (car binding))) - (doc (embark--function-doc cmd))) - (push (format "%s%s\n" keys - (propertize - (car (split-string (or doc "") "\n")) - 'face 'embark-verbose-indicator-documentation)) - result))))))) - -(defun embark--verbose-indicator-update (keymap targets) - "Update verbose indicator buffer. -The arguments are the new KEYMAP and TARGETS." - (with-current-buffer (get-buffer-create embark--verbose-indicator-buffer) - (let* ((inhibit-read-only t) - (bindings - (embark--formatted-bindings keymap embark-verbose-indicator-nested)) - (bindings (car bindings)) - (shadowed-targets (mapcar - (lambda (x) (symbol-name (plist-get x :type))) - (cdr targets))) - (cycle (let ((ck (where-is-internal #'embark-cycle keymap))) - (and ck (key-description (car ck)))))) - (setq-local cursor-type nil) - (setq-local truncate-lines t) - (setq-local buffer-read-only t) - (erase-buffer) - (dolist (section embark-verbose-indicator-buffer-sections) - (insert - (if (stringp section) - section - (or (funcall - (let ((prefixed (intern (format - "embark--verbose-indicator-section-%s" - section)))) - (cond - ((fboundp prefixed) prefixed) - ((fboundp section) section) - (t (error "Undefined verbose indicator section `%s'" - section)))) - :targets targets :shadowed-targets shadowed-targets - :bindings bindings :cycle cycle) - "")))) - (goto-char (point-min))))) - -(defun embark-verbose-indicator () - "Indicator that displays a table of key bindings in a buffer. -The default display includes the type and value of the current -target, the list of other target types, and a table of key -bindings, actions and the first line of their docstrings. - -The order and formatting of these items is completely -configurable through the variable -`embark-verbose-indicator-buffer-sections'. - -If the keymap being shown contains prefix keys, the table of key -bindings can either show just the prefixes and update once the -prefix is pressed, or it can contain a flat list of all full key -sequences bound in the keymap. This is controlled by the -variable `embark-verbose-indicator-nested'. - -To reduce clutter in the key binding table, one can set the -variable `embark-verbose-indicator-excluded-actions' to a list -of symbols and regexps matching commands to exclude from the -table. - -To configure how a window is chosen to display this buffer, see -the variable `embark-verbose-indicator-display-action'." - (lambda (&optional keymap targets prefix) - (if (not keymap) - (when-let ((win (get-buffer-window embark--verbose-indicator-buffer - 'visible))) - (quit-window 'kill-buffer win)) - (embark--verbose-indicator-update - (if (and prefix embark-verbose-indicator-nested) - ;; Lookup prefix keymap globally if not found in action keymap - (let ((overriding-terminal-local-map keymap)) - (key-binding prefix 'accept-default)) - keymap) - targets) - (let ((display-buffer-alist - `(,@display-buffer-alist - (,(regexp-quote embark--verbose-indicator-buffer) - ,@embark-verbose-indicator-display-action)))) - (display-buffer embark--verbose-indicator-buffer))))) - -(defcustom embark-mixed-indicator-delay 0.5 - "Time in seconds after which the verbose indicator is shown. -The mixed indicator starts by showing the minimal indicator and -after this delay shows the verbose indicator." - :type '(choice (const :tag "No delay" 0) - (number :tag "Delay in seconds"))) - -(defcustom embark-mixed-indicator-both nil - "Show both indicators, even after the verbose indicator appeared." - :type 'boolean) - -(defun embark-mixed-indicator () - "Mixed indicator showing keymap and targets. -The indicator shows the `embark-minimal-indicator' by default. -After `embark-mixed-indicator-delay' seconds, the -`embark-verbose-indicator' is shown. This which-key-like approach -ensures that Embark stays out of the way for quick actions. The -helpful keybinding reminder still pops up automatically without -further user intervention." - (let ((vindicator (embark-verbose-indicator)) - (mindicator (embark-minimal-indicator)) - vindicator-active - vtimer) - (lambda (&optional keymap targets prefix) - ;; Always cancel the timer. - ;; 1. When updating, cancel timer, since the user has pressed - ;; a key before the timer elapsed. - ;; 2. For cleanup, the timer must also be canceled. - (when vtimer - (cancel-timer vtimer) - (setq vtimer nil)) - (if (not keymap) - (progn - (funcall vindicator) - (when mindicator - (funcall mindicator))) - (when mindicator - (funcall mindicator keymap targets prefix)) - (if vindicator-active - (funcall vindicator keymap targets prefix) - (setq vtimer - (run-at-time - embark-mixed-indicator-delay nil - (lambda () - (when (and (not embark-mixed-indicator-both) mindicator) - (funcall mindicator) - (setq mindicator nil)) - (setq vindicator-active t) - (funcall vindicator keymap targets prefix))))))))) - -;;;###autoload -(defun embark-bindings-in-keymap (keymap) - "Explore command key bindings in KEYMAP with `completing-read'. -The selected command will be executed. Interactively, prompt the -user for a KEYMAP variable." - (interactive - (list - (symbol-value - (intern-soft - (completing-read - "Keymap: " - (embark--with-category - 'variable - (cl-loop for x being the symbols - if (and (boundp x) (keymapp (symbol-value x))) - collect (symbol-name x))) - nil t nil 'variable-name-history - (let ((major-mode-map - (concat (symbol-name major-mode) "-map"))) - (when (intern-soft major-mode-map) major-mode-map))))))) - (when-let (command (embark-completing-read-prompter keymap nil 'no-default)) - (call-interactively command))) - -;;;###autoload -(defun embark-bindings (global) - "Explore current command key bindings with `completing-read'. -The selected command will be executed. - -This shows key bindings from minor mode maps and the local -map (usually set by the major mode), but also less common keymaps -such as those from a text property or overlay, or the overriding -maps: `overriding-terminal-local-map' and `overriding-local-map'. - -Additionally, if GLOBAL is non-nil (interactively, if called with -a prefix argument), this command includes global key bindings." - (interactive "P") - (embark-bindings-in-keymap - (make-composed-keymap - (let ((all-maps (current-active-maps t))) - (if global all-maps (remq global-map all-maps)))))) - -;;;###autoload -(defun embark-bindings-at-point () - "Explore all key bindings at point with `completing-read'. -The selected command will be executed. - -This command lists key bindings found in keymaps specified by the -text properties `keymap' or `local-map', from either buffer text -or an overlay. These are not widely used in Emacs, and when they -are used can be somewhat hard to discover. Examples of locations -that have such a keymap are links and images in `eww' buffers, -attachment links in `gnus' article buffers, and the stash line -in a `vc-dir' buffer." - (interactive) - (if-let ((keymaps (delq nil (list (get-char-property (point) 'keymap) - (get-char-property (point) 'local-map))))) - (embark-bindings-in-keymap (make-composed-keymap keymaps)) - (user-error "No key bindings found at point"))) - -;;;###autoload -(defun embark-prefix-help-command () - "Prompt for and run a command bound in the prefix used for this command. -The prefix described consists of all but the last event of the -key sequence that ran this command. This function is intended to -be used as a value for `prefix-help-command'. - -In addition to using completion to select a command, you can also -type @ and the key binding (without the prefix)." - (interactive) - (when-let ((keys (this-command-keys-vector)) - (prefix (seq-take keys (1- (length keys)))) - (keymap (key-binding prefix 'accept-default))) - (minibuffer-with-setup-hook - (lambda () - (let ((pt (- (minibuffer-prompt-end) 2))) - (overlay-put (make-overlay pt pt) 'before-string - (format " under %s" (key-description prefix))))) - (embark-bindings-in-keymap keymap)))) - -(defun embark--prompt (indicators keymap targets) - "Call the prompter with KEYMAP and INDICATORS. -The TARGETS are displayed for actions outside the minibuffer." - (mapc (lambda (i) (funcall i keymap targets)) indicators) - (condition-case nil - (minibuffer-with-setup-hook - (lambda () - ;; if the prompter opens its own minibuffer, show - ;; the indicator there too - (let ((inner-indicators (mapcar #'funcall embark-indicators))) - (mapc (lambda (i) (funcall i keymap targets)) inner-indicators) - (add-hook 'minibuffer-exit-hook - (lambda () (mapc #'funcall inner-indicators)) - nil t))) - (let ((enable-recursive-minibuffers t)) - (funcall embark-prompter keymap - (lambda (prefix) - (mapc (lambda (i) (funcall i keymap targets prefix)) - indicators))))) - (quit nil))) - -(defvar embark--run-after-command-functions nil - "Abnormal hook, used by `embark--run-after-command'.") - -(defun embark--run-after-command (fn &rest args) - "Call FN with ARGS after the current commands finishes. -If multiple functions are queued with this function during the -same command, they will be called in the order from the one -queued most recently to the one queued least recently." - ;; We don't simply add FN to `post-command-hook' because FN may recursively - ;; call this function. In that case, FN would modify `post-command-hook' - ;; from within post-command-hook, which doesn't behave properly in our case. - ;; We use our own abnormal hook and run it from PCH in a way that it is OK to - ;; modify it from within its own functions. - (unless embark--run-after-command-functions - (let (pch timer has-run) - (setq pch - (lambda () - (remove-hook 'post-command-hook pch) - (cancel-timer timer) - (unless has-run - (setq has-run t) - (while embark--run-after-command-functions - ;; The following funcall may recursively call - ;; `embark--run-after-command', modifying - ;; `embark--run-after-command-functions'. This is why this - ;; loop has to be implemented carefully. We have to pop the - ;; function off the hook before calling it. Using `dolist' - ;; on the hook would also be incorrect, because it wouldn't - ;; take modifications of this hook into account. - (with-demoted-errors "embark PCH: %S" - (condition-case nil - (funcall (pop embark--run-after-command-functions)) - (quit (message "Quit")))))))) - (add-hook 'post-command-hook pch 'append) - ;; Generally we prefer `post-command-hook' because it plays well with - ;; keyboard macros. In some cases, `post-command-hook' isn't run after - ;; exiting a recursive edit, so set up the following timer as a backup. - (setq timer (run-at-time 0 nil pch)))) - - ;; Keep the default-directory alive, since this is often overwritten, - ;; for example by Consult commands. - ;; TODO it might be necessary to add more dynamically bound variables - ;; here. What we actually want are functions `capture-dynamic-scope' - ;; and `eval-in-dynamic-scope', but this does not exist? - (let ((dir default-directory)) - (push (lambda () - (let ((default-directory dir)) - (apply fn args))) - embark--run-after-command-functions))) - -(defun embark--quit-and-run (fn &rest args) - "Quit the minibuffer and then call FN with ARGS. -If called outside the minibuffer, simply apply FN to ARGS." - (if (not (minibufferp)) - (apply fn args) - (apply #'embark--run-after-command fn args) - (embark--run-after-command #'set 'ring-bell-function ring-bell-function) - (setq ring-bell-function #'ignore) - (if (fboundp 'minibuffer-quit-recursive-edit) - (minibuffer-quit-recursive-edit) - (abort-recursive-edit)))) - -(defun embark--run-action-hooks (hooks action target quit) - "Run HOOKS for ACTION. -The HOOKS argument must be alist. The keys t and :always are -treated specially. The :always hooks are executed always and the -t hooks are the default hooks, for when there are no -command-specific hooks for ACTION. The QUIT, ACTION and TARGET -arguments are passed to the hooks as keyword arguments." - (mapc (lambda (h) (apply h :action action :quit quit target)) - (or (alist-get action hooks) - (alist-get t hooks))) - (mapc (lambda (h) (apply h :action action :quit quit target)) - (alist-get :always hooks))) - -(defun embark--run-around-action-hooks - (action target quit &optional non-interactive) - "Run the `embark-around-action-hooks' for ACTION. -All the applicable around hooks are composed in the order they -are present in `embark-around-action-hooks'. The keys t and -:always in `embark-around-action-hooks' are treated specially. -The :always hooks are executed always (outermost) and the t hooks -are the default hooks, for when there are no command-specific -hooks for ACTION. The QUIT, ACTION and TARGET arguments are -passed to the hooks as keyword arguments. - -The optional argument NON-INTERACTIVE controls whether the action -is run with `command-execute' or with `funcall' passing the -target as argument." - (apply - (seq-reduce - (lambda (fn hook) - (lambda (&rest args) (apply hook (plist-put args :run fn)))) - (let ((hooks embark-around-action-hooks)) - (reverse - (append (or (alist-get action hooks) (alist-get t hooks)) - (alist-get :always hooks)))) - (if non-interactive - (lambda (&rest args) - (funcall (plist-get args :action) - (or (plist-get args :candidates) (plist-get args :target)))) - (lambda (&rest args) - (command-execute (plist-get args :action))))) - :action action :quit quit target)) - -(defun embark--act (action target &optional quit) - "Perform ACTION injecting the TARGET. -If called from a minibuffer with non-nil QUIT, quit the -minibuffer before executing the action." - (if (memq action '(embark-become ; these actions should run in - embark-collect ; the current buffer, not the - embark-live ; target buffer - embark-export - embark-select - embark-act-all)) - (progn - (embark--run-action-hooks embark-pre-action-hooks action target quit) - (unwind-protect (embark--run-around-action-hooks action target quit) - (embark--run-action-hooks embark-post-action-hooks - action target quit))) - (let* ((command embark--command) - (prefix prefix-arg) - (action-window (embark--target-window t)) - (directory default-directory) - (inject - (lambda () - (let ((contents (minibuffer-contents))) - (delete-minibuffer-contents) - (insert - (propertize - (substring-no-properties (plist-get target :target)) - 'embark--initial-input contents))) - (if (memq 'ivy--queue-exhibit post-command-hook) - ;; Ivy has special needs: (1) for file names - ;; ivy-immediate-done is not equivalent to - ;; exit-minibuffer, (2) it needs a chance to run - ;; its post command hook first, so use depth 10 - (add-hook 'post-command-hook 'ivy-immediate-done 10 t) - (add-hook 'post-command-hook #'exit-minibuffer nil t)) - (embark--run-action-hooks embark-target-injection-hooks - action target quit))) - (dedicate (and (derived-mode-p 'embark-collect-mode) - (not (window-dedicated-p)) - (selected-window))) - (multi (memq action embark-multitarget-actions)) - (run-action - (if (and (commandp action) (not multi)) - (lambda () - (let (final-window) - (when dedicate (set-window-dedicated-p dedicate t)) - (unwind-protect - (with-selected-window action-window - (let ((enable-recursive-minibuffers t) - (embark--command command) - (prefix-arg prefix) - ;; the next two avoid mouse dialogs - (use-dialog-box nil) - (last-nonmenu-event 13) - (default-directory directory)) - (embark--run-action-hooks embark-pre-action-hooks - action target quit) - (minibuffer-with-setup-hook inject - ;; pacify commands that use (this-command-keys) - (when (= (length (this-command-keys)) 0) - (set--this-command-keys - (if (characterp last-command-event) - (string last-command-event) - "\r"))) - (setq this-command action) - (embark--run-around-action-hooks - action target quit))) - (setq final-window (selected-window))) - (embark--run-action-hooks embark-post-action-hooks - action target quit) - (when dedicate (set-window-dedicated-p dedicate nil))) - (unless (eq final-window action-window) - (select-window final-window)))) - (let ((target - (if (and multi (null (plist-get target :candidates))) - (plist-put - target :candidates (list (plist-get target :target))) - target))) - (lambda () - (with-selected-window action-window - (embark--run-action-hooks embark-pre-action-hooks - action target quit) - (unwind-protect - (let ((current-prefix-arg prefix) - (default-directory directory)) - (embark--run-around-action-hooks - action target quit :non-interactive)) - (embark--run-action-hooks embark-post-action-hooks - action target quit)))))))) - (setq prefix-arg nil) - (if quit (embark--quit-and-run run-action) (funcall run-action))))) - -(defun embark--refine-multi-category (_type target) - "Refine `multi-category' TARGET to its actual type." - (or (let ((mc (get-text-property 0 'multi-category target))) - (cond - ;; The `cdr' of the `multi-category' property can be a buffer object. - ((and (eq (car mc) 'buffer) (buffer-live-p (cdr mc))) - (cons 'buffer (buffer-name (cdr mc)))) - ((stringp (cdr mc)) mc))) - (cons 'general target))) - -(defun embark--simplify-path (_type target) - "Simplify and '//' or '~/' in the TARGET file path." - (cons 'file (substitute-in-file-name target))) - -(defun embark--keybinding-command (_type target) - "Treat an `embark-keybinding' TARGET as a command." - (when-let ((cmd (get-text-property 0 'embark-command target))) - (cons 'command (format "%s" cmd)))) - -(defun embark--lookup-lighter-minor-mode (_type target) - "If TARGET is a lighter, look up its minor mode. - -The `describe-minor-mode' command has as completion candidates -both minor-modes and their lighters. This function replaces the -lighters by their minor modes, so actions expecting a function -work on them." - (cons 'minor-mode - (let ((symbol (intern-soft target))) - (if (and symbol (boundp symbol)) - target - (symbol-name (lookup-minor-mode-from-indicator target)))))) - -(declare-function project-current "project") -(declare-function project-roots "project") -(declare-function project-root "project") - -(defun embark--project-file-full-path (_type target) - "Get full path of project file TARGET." - ;; TODO project-find-file can be called from outside all projects in - ;; which case it prompts for a project first; we don't support that - ;; case yet, since there is no current project. - (cons 'file - (if-let ((project (project-current)) - (root (if (fboundp 'project-root) - (project-root project) - (with-no-warnings - (car (project-roots project)))))) - (expand-file-name target root) - target))) - -(defun embark--remove-package-version (_type target) - "Remove version number from a versioned package TARGET." - (cons 'package (replace-regexp-in-string "-[0-9.]+$" "" target))) - -(defun embark--targets () - "Retrieve current targets. - -An initial guess at the current targets and their types is -determined by running the functions in `embark-target-finders'. -Each function should either return nil, a pair of a type symbol -and target string or a triple of a type symbol, target string and -target bounds. - -In the minibuffer only the first target finder returning non-nil -is taken into account. When finding targets at point in other -buffers, all target finder functions are executed. - -For each target, the type is then looked up as a key in the -variable `embark-transformer-alist'. If there is a transformer -for the type, it is called with the type and target, and must -return a `cons' of the transformed type and transformed target. - -The return value of `embark--targets' is a list of plists. Each -plist concerns one target, and has keys `:type', `:target', -`:orig-type', `:orig-target' and `:bounds'." - (let (targets) - (run-hook-wrapped - 'embark-target-finders - (lambda (fun) - (dolist (found (when-let (result (funcall fun)) - (if (consp (car result)) result (list result)))) - (let* ((type (or (car found) 'general)) - (target+bounds (cdr found)) - (target (if (consp target+bounds) - (car target+bounds) - target+bounds)) - (bounds (and (consp target+bounds) (cdr target+bounds))) - (full-target - (append - (list :orig-type type :orig-target target :bounds bounds) - (if-let (transform (alist-get type embark-transformer-alist)) - (let ((trans (funcall transform type target))) - (list :type (car trans) :target (cdr trans))) - (list :type type :target target))))) - (push full-target targets))) - (and targets (minibufferp)))) - (nreverse - (cl-delete-duplicates ; keeps last duplicate, but we reverse - targets - :test (lambda (t1 t2) - (and (equal (plist-get t1 :target) (plist-get t2 :target)) - (eq (plist-get t1 :type) (plist-get t2 :type)))))))) - -(defun embark--default-action (type) - "Return default action for the given TYPE of target. -The most common case is that the target comes from minibuffer -completion, in which case the default action is the command that -opened the minibuffer in the first place. This can be overridden -by `embark-default-action-overrides'. - -For targets that do not come from minibuffer completion -\(typically some thing at point in a regular buffer) and whose -type is not listed in `embark-default-action-overrides', the -default action is given by whatever binding RET has in the action -keymap for the given type." - (or (alist-get (cons type embark--command) embark-default-action-overrides - nil nil #'equal) - (alist-get type embark-default-action-overrides) - (alist-get t embark-default-action-overrides) - embark--command - (lookup-key (embark--raw-action-keymap type) "\r"))) - -(defun embark--rotate (list k) - "Rotate LIST by K elements and return the rotated list." - (setq k (mod k (length list))) - (append (seq-drop list k) (seq-take list k))) - -(defun embark--orig-target (target) - "Convert TARGET to original target." - (plist-put - (plist-put - (copy-sequence target) - :target (plist-get target :orig-target)) - :type (plist-get target :orig-type))) - -(defun embark--quit-p (action) - "Determine whether to quit the minibuffer after ACTION. -This function consults `embark-quit-after-action' to decide -whether or not the user wishes to quit the minibuffer after -performing the ACTION, assuming this is done from a minibuffer." - (let* ((cfg embark-quit-after-action) - (quit (if (consp cfg) (alist-get action cfg (alist-get t cfg)) cfg))) - (when embark--toggle-quit (setq quit (not quit))) - (setq embark--toggle-quit nil) - quit)) - -;;;###autoload -(defun embark-act (&optional arg) - "Prompt the user for an action and perform it. -The targets of the action are chosen by `embark-target-finders'. -By default, if called from a minibuffer the target is the top -completion candidate. When called from a non-minibuffer buffer -there can multiple targets and you can cycle among them by using -`embark-cycle' (which is bound by default to the same key -binding `embark-act' is, but see `embark-cycle-key'). - -This command uses `embark-prompter' to ask the user to specify an -action, and calls it injecting the target at the first minibuffer -prompt. - -If you call this from the minibuffer, it can optionally quit the -minibuffer. The variable `embark-quit-after-action' controls -whether calling `embark-act' with nil ARG quits the minibuffer, -and if ARG is non-nil it will do the opposite. Interactively, -ARG is the prefix argument. - -If instead you call this from outside the minibuffer, the first -ARG targets are skipped over (if ARG is negative the skipping is -done by cycling backwards) and cycling starts from the following -target." - (interactive "P") - (let* ((targets (or (embark--targets) (user-error "No target found"))) - (indicators (mapcar #'funcall embark-indicators)) - (default-done nil)) - (when arg - (if (minibufferp) - (embark-toggle-quit) - (setq targets (embark--rotate targets (prefix-numeric-value arg))))) - (unwind-protect - (while - (let* ((target (car targets)) - (action - (or (embark--prompt - indicators - (let ((embark-default-action-overrides - (if default-done - `((t . ,default-done)) - embark-default-action-overrides))) - (embark--action-keymap (plist-get target :type) - (cdr targets))) - targets) - (user-error "Canceled"))) - (default-action (or default-done - (embark--default-action - (plist-get target :type))))) - (cond - ;; When acting twice in the minibuffer, do not restart - ;; `embark-act'. Otherwise the next `embark-act' will - ;; find a target in the original buffer. - ((eq action #'embark-act) - (message "Press an action key")) - ((eq action #'embark-cycle) - (setq targets (embark--rotate - targets (prefix-numeric-value prefix-arg)))) - (t - ;; if the action is non-repeatable, cleanup indicator now - (let ((repeat (embark--action-repeatable-p action))) - (unless repeat (mapc #'funcall indicators)) - (condition-case err - (embark--act - action - (if (and (eq action default-action) - (eq action embark--command) - (not (memq action embark-multitarget-actions))) - (embark--orig-target target) - target) - (embark--quit-p action)) - (user-error - (funcall (if repeat #'message #'user-error) - "%s" (cadr err)))) - (when-let (new-targets (and repeat (embark--targets))) - ;; Terminate repeated prompter on default action, - ;; when repeating. Jump to the region type if the - ;; region is active after the action, or else to the - ;; current type again. - (setq default-done #'embark-done - targets - (embark--rotate - new-targets - (or (cl-position-if - (let ((desired-type - (if (eq repeat t) - (plist-get (car targets) :type) - repeat))) - (lambda (x) - (eq (plist-get x :type) desired-type))) - new-targets) - 0))))))))) - (mapc #'funcall indicators)))) - -(defun embark--maybe-transform-candidates () - "Collect candidates and see if they all transform to the same type. -Return a plist with keys `:type', `:orig-type', `:candidates', and -`:orig-candidates'." - (pcase-let* ((`(,type . ,candidates) - (run-hook-with-args-until-success 'embark-candidate-collectors)) - (bounds (mapcar #'cdr-safe candidates))) - (setq candidates - (mapcar (lambda (x) (if (consp x) (car x) x)) candidates)) - (when (eq type 'file) - (let ((dir (embark--default-directory))) - (setq candidates - (mapcar (lambda (cand) - (abbreviate-file-name - (expand-file-name (substitute-in-file-name cand) dir))) - candidates)))) - ;; TODO more systematic approach to applying substitute-in-file-name - (append - (list :orig-type type :orig-candidates candidates :bounds bounds) - (or (when candidates - (when-let ((transformer (alist-get type embark-transformer-alist))) - (pcase-let* ((`(,new-type . ,first-cand) - (funcall transformer type (car candidates)))) - (let ((new-candidates (list first-cand))) - (when (cl-every - (lambda (cand) - (pcase-let ((`(,t-type . ,t-cand) - (funcall transformer type cand))) - (when (eq t-type new-type) - (push t-cand new-candidates) - t))) - (cdr candidates)) - (list :type new-type - :candidates (nreverse new-candidates))))))) - (list :type type :candidates candidates))))) - -;;;###autoload -(defun embark-act-all (&optional arg) - "Prompt the user for an action and perform it on each candidate. -The candidates are chosen by `embark-candidate-collectors'. By -default, if `embark-select' has been used to select some -candidates, then `embark-act-all' will act on those candidates; -otherwise, if the selection is empty and `embark-act-all' is -called from a minibuffer, then the candidates are the completion -candidates. - -This command uses `embark-prompter' to ask the user to specify an -action, and calls it injecting the target at the first minibuffer -prompt. - -If you call this from the minibuffer, it can optionally quit the -minibuffer. The variable `embark-quit-after-action' controls -whether calling `embark-act' with nil ARG quits the minibuffer, -and if ARG is non-nil it will do the opposite. Interactively, -ARG is the prefix argument." - (interactive "P") - (let* ((transformed (embark--maybe-transform-candidates)) - (type (plist-get transformed :type)) - (orig-type (plist-get transformed :orig-type)) - (candidates - (or (cl-mapcar - (lambda (cand orig-cand bounds) - (list :type type :target cand - :bounds (when bounds - (cons (copy-marker (car bounds)) - (copy-marker (cdr bounds)))) - :orig-type orig-type :orig-target orig-cand)) - (plist-get transformed :candidates) - (plist-get transformed :orig-candidates) - (plist-get transformed :bounds)) - (user-error "No candidates to act on"))) - (indicators (mapcar #'funcall embark-indicators))) - (when arg (embark-toggle-quit)) - (unwind-protect - (let* ((action - (or (embark--prompt - indicators (embark--action-keymap type nil) - (list (list :type type :multi (length candidates)))) - (user-error "Canceled"))) - (prefix prefix-arg) - (act (lambda (candidate) - (cl-letf (((symbol-function 'embark--restart) #'ignore) - ((symbol-function 'embark--confirm) #'ignore)) - (let ((prefix-arg prefix)) - (when-let ((bounds (plist-get candidate :bounds))) - (goto-char (car bounds))) - (embark--act action candidate))))) - (quit (embark--quit-p action))) - (when (and (eq action (embark--default-action type)) - (eq action embark--command)) - (setq candidates (mapcar #'embark--orig-target candidates))) - (when (or (not (or embark-confirm-act-all - (memq 'embark--confirm - (alist-get action embark-pre-action-hooks)))) - (y-or-n-p (format "Run %s on %d %ss? " - action (length candidates) type))) - (if (memq action embark-multitarget-actions) - (let ((prefix-arg prefix)) - (embark--act action transformed quit)) - (save-excursion - (if quit - (embark--quit-and-run #'mapc act candidates) - (mapc act candidates)))) - (when (and (not quit) - (memq 'embark--restart - (alist-get action embark-post-action-hooks))) - (embark--restart)))) - (dolist (cand candidates) - (when-let ((bounds (plist-get cand :bounds))) - (set-marker (car bounds) nil) ; yay, manual memory management! - (set-marker (cdr bounds) nil))) - (setq prefix-arg nil) - (mapc #'funcall indicators)))) - -(defun embark-highlight-indicator () - "Action indicator highlighting the target at point." - (let (overlay) - (lambda (&optional keymap targets _prefix) - (let ((bounds (plist-get (car targets) :bounds))) - (when (and overlay (or (not keymap) (not bounds))) - (delete-overlay overlay) - (setq overlay nil)) - (when bounds - (if overlay - (move-overlay overlay (car bounds) (cdr bounds)) - (setq overlay (make-overlay (car bounds) (cdr bounds))) - (overlay-put overlay 'category 'embark-target-overlay)) - (overlay-put overlay 'window (selected-window))))))) - -(defun embark-isearch-highlight-indicator () - "Action indicator highlighting all occurrences of the identifier at point. -This indicator only does something for targets which are -identifiers or symbols. For those it uses `isearch''s lazy -highlighting feature to highlight all occurrences of the target in -the buffer. This indicator is best used in conjunction with -`embark-highlight-indicator': by using them both you get the -target and the other occurrences of it highlighted in different -colors." - (lambda (&optional _keymap targets _prefix) - (if (and (not (minibufferp)) - (memq (plist-get (car targets) :orig-type) '(symbol identifier))) - (let ((isearch-string (plist-get (car targets) :target)) - (isearch-regexp-function #'isearch-symbol-regexp)) - (isearch-lazy-highlight-new-loop)) - (setq isearch-lazy-highlight-last-string nil) - (lazy-highlight-cleanup t)))) - -(defun embark-cycle (_arg) - "Cycle over the next ARG targets at point. -If ARG is negative, cycle backwards." - (interactive "p") - (user-error "Not meant to be called directly")) - -(defun embark-done () - "Terminate sequence of repeated actions." - (interactive)) - -;;;###autoload -(defun embark-dwim (&optional arg) - "Run the default action on the current target. -The target of the action is chosen by `embark-target-finders'. - -If the target comes from minibuffer completion, then the default -action is the command that opened the minibuffer in the first -place, unless overridden by `embark-default-action-overrides'. - -For targets that do not come from minibuffer completion -\(typically some thing at point in a regular buffer) and whose -type is not listed in `embark-default-action-overrides', the -default action is given by whatever binding RET has in the action -keymap for the target's type. - -See `embark-act' for the meaning of the prefix ARG." - (interactive "P") - (if-let ((targets (embark--targets))) - (let* ((target - (or (nth - (if (or (null arg) (minibufferp)) - 0 - (mod (prefix-numeric-value arg) (length targets))) - targets))) - (type (plist-get target :type)) - (default-action (embark--default-action type)) - (action (or (command-remapping default-action) default-action))) - (unless action - (user-error "No default action for %s targets" type)) - (when (and arg (minibufferp)) (setq embark--toggle-quit t)) - (embark--act action - (if (and (eq default-action embark--command) - (not (memq default-action - embark-multitarget-actions))) - (embark--orig-target target) - target) - (embark--quit-p action))) - (user-error "No target found"))) - -(defun embark--become-keymap () - "Return keymap of commands to become for current command." - (let ((map (make-composed-keymap - (cl-loop for keymap-name in embark-become-keymaps - for keymap = (symbol-value keymap-name) - when (where-is-internal embark--command (list keymap)) - collect keymap)))) - (when embark-help-key - (keymap-set map embark-help-key #'embark-keymap-help)) - map)) - -;;;###autoload -(defun embark-become (&optional full) - "Make current command become a different command. -Take the current minibuffer input as initial input for new -command. The new command can be run normally using key bindings or -\\[execute-extended-command], but if the current command is found in a keymap in -`embark-become-keymaps', that keymap is activated to provide -convenient access to the other commands in it. - -If FULL is non-nil (interactively, if called with a prefix -argument), the entire minibuffer contents are used as the initial -input of the new command. By default only the part of the -minibuffer contents between the current completion boundaries is -taken. What this means is fairly technical, but (1) usually -there is no difference: the completion boundaries include the -entire minibuffer contents, and (2) the most common case where -these notions differ is file completion, in which case the -completion boundaries single out the path component containing -point." - (interactive "P") - (unless (minibufferp) - (user-error "Not in a minibuffer")) - (let* ((target (embark--display-string ; remove invisible portions - (if full - (minibuffer-contents) - (pcase-let ((`(,beg . ,end) (embark--boundaries))) - (substring (minibuffer-contents) beg - (+ end (embark--minibuffer-point))))))) - (keymap (embark--become-keymap)) - (targets `((:type embark-become :target ,target))) - (indicators (mapcar #'funcall embark-indicators)) - (become (unwind-protect - (embark--prompt indicators keymap targets) - (mapc #'funcall indicators)))) - (unless become - (user-error "Canceled")) - (embark--become-command become target))) - -(defun embark--become-command (command input) - "Quit current minibuffer and start COMMAND with INPUT." - (embark--quit-and-run - (lambda () - (minibuffer-with-setup-hook - (lambda () - (delete-minibuffer-contents) - (insert input)) - (let ((use-dialog-box nil) ;; avoid mouse dialogs - (last-nonmenu-event 13)) - (setq this-command command) - (command-execute command)))))) - -;;; Embark collect - -(defgroup embark-collect nil - "Buffers for acting on collected Embark targets." - :group 'embark) - -(defcustom embark-candidate-collectors - '(embark-selected-candidates - embark-minibuffer-candidates - embark-completion-list-candidates - embark-dired-candidates - embark-ibuffer-candidates - embark-embark-collect-candidates - embark-custom-candidates) - "List of functions that collect all candidates in a given context. -These are used to fill an Embark Collect buffer. Each function -should return either nil (to indicate it found no candidates) or -a list whose first element is a symbol indicating the type of -candidates and whose `cdr' is the list of candidates, each of -which should be either a string or a dotted list of the -form (TARGET START . END), where START and END are the buffer -positions bounding the TARGET string." - :type 'hook) - -(defcustom embark-exporters-alist - '((buffer . embark-export-ibuffer) - (file . embark-export-dired) - (package . embark-export-list-packages) - (bookmark . embark-export-bookmarks) - (variable . embark-export-customize-variable) - (face . embark-export-customize-face) - (symbol . embark-export-apropos) - (minor-mode . embark-export-apropos) - (function . embark-export-apropos) - (command . embark-export-apropos) - (t . embark-collect)) - "Alist associating completion types to export functions. -Each function should take a list of strings which are candidates -for actions and make a buffer appropriate to manage them. For -example, the default is to make a Dired buffer for files, and an -ibuffer for buffers. - -The key t is also allowed in the alist, and the corresponding -value indicates the default function to use for other types. The -default is `embark-collect'" - :type '(alist :key-type symbol :value-type function)) - -(defcustom embark-after-export-hook nil - "Hook run after `embark-export' in the newly created buffer." - :type 'hook) - -(defface embark-collect-candidate '((t :inherit default)) - "Face for candidates in Embark Collect buffers.") - -(defface embark-collect-group-title - '((t :inherit shadow :slant italic)) - "Face for group titles in Embark Collect buffers.") - -(defface embark-collect-group-separator - '((t :inherit shadow :strike-through t italic)) - "Face for group titles in Embark Collect buffers.") - -(defcustom embark-collect-group-format - (concat - (propertize " " 'face 'embark-collect-group-separator) - (propertize " %s " 'face 'embark-collect-group-title) - (propertize " " 'face 'completions-group-separator - 'display '(space :align-to right))) - "Format string used for the group title in Embark Collect buffers." - :type 'string) - -(defface embark-collect-annotation '((t :inherit completions-annotations)) - "Face for annotations in Embark Collect. -This is only used for annotation that are not already fontified.") - -(defvar-local embark--rerun-function nil - "Function to rerun the collect or export that made the current buffer.") - -(autoload 'package-delete "package") -(declare-function package--from-builtin "package") -(declare-function package-desc-extras "package") -(declare-function package-desc-name "package") -(defvar package--builtins) -(defvar package-alist) -(defvar package-archive-contents) -(defvar package--initialized) - -(defun embark--package-desc (pkg) - "Return the description structure for package PKG." - (or ; found this in `describe-package-1' - (car (alist-get pkg package-alist)) - (if-let ((built-in (assq pkg package--builtins))) - (package--from-builtin built-in) - (car (alist-get pkg package-archive-contents))))) - -(defun embark-minibuffer-candidates () - "Return all current completion candidates from the minibuffer." - (when (minibufferp) - (let* ((all (completion-all-completions - (minibuffer-contents) - minibuffer-completion-table - minibuffer-completion-predicate - (embark--minibuffer-point))) - (last (last all))) - (when last (setcdr last nil)) - (cons - (completion-metadata-get (embark--metadata) 'category) - all)))) - -(defun embark-sorted-minibuffer-candidates () - "Return a sorted list of current minibuffer completion candidates. -This using the same sort order that `icomplete' and -`minibuffer-force-complete' use. The intended usage is that you -replace `embark-minibuffer-candidates' with this function in the -list `embark-candidate-collectors'." - (when (minibufferp) - (cons - (completion-metadata-get (embark--metadata) 'category) - (nconc (cl-copy-list (completion-all-sorted-completions)) nil)))) - -(declare-function dired-get-marked-files "dired") -(declare-function dired-move-to-filename "dired") -(declare-function dired-move-to-end-of-filename "dired") - -(defun embark-dired-candidates () - "Return marked or all files shown in Dired buffer. -If any buffer is marked, return marked buffers; otherwise, return -all buffers." - (when (derived-mode-p 'dired-mode) - (cons 'file - (or - ;; dired-get-marked-files returns the file on the current - ;; line if no marked files are found; and when the fourth - ;; argument is non-nil, the "no marked files" case is - ;; distinguished from the "single marked file" case by - ;; returning (list t marked-file) in the latter - (let ((marked (dired-get-marked-files t nil nil t))) - (and (cdr marked) - (if (eq (car marked) t) (cdr marked) marked))) - (save-excursion - (goto-char (point-min)) - (let (files) - (while (not (eobp)) - (when-let (file (dired-get-filename t t)) - (push `(,file - ,(progn (dired-move-to-filename) (point)) - . ,(progn (dired-move-to-end-of-filename t) (point))) - files)) - (forward-line)) - (nreverse files))))))) - -(autoload 'ibuffer-marked-buffer-names "ibuffer") -(declare-function ibuffer-map-lines-nomodify "ibuffer") - -(defun embark-ibuffer-candidates () - "Return marked or all buffers listed in ibuffer buffer. -If any buffer is marked, return marked buffers; otherwise, return -all buffers." - (when (derived-mode-p 'ibuffer-mode) - (cons 'buffer - (or (ibuffer-marked-buffer-names) - (let (buffers) - (ibuffer-map-lines-nomodify - (lambda (buffer _mark) - (push (buffer-name buffer) buffers))) - (nreverse buffers)))))) - -(defun embark-embark-collect-candidates () - "Return candidates in Embark Collect buffer. -This makes `embark-export' work in Embark Collect buffers." - (when (derived-mode-p 'embark-collect-mode) - (cons embark--type - (save-excursion - (goto-char (point-min)) - (let (all) - (when-let ((cand (embark-target-collect-candidate))) - (push (cdr cand) all)) - (while (forward-button 1 nil nil t) - (when-let ((cand (embark-target-collect-candidate))) - (push (cdr cand) all))) - (nreverse all)))))) - -(defun embark-completion-list-candidates () - "Return all candidates in a completions buffer." - (when (derived-mode-p 'completion-list-mode) - (cons - embark--type - (save-excursion - (goto-char (point-min)) - (next-completion 1) - (let (all) - (while (not (eobp)) - (push (cdr (embark-target-completion-list-candidate)) all) - (next-completion 1)) - (nreverse all)))))) - -(defun embark-custom-candidates () - "Return all variables and faces listed in this `Custom-mode' buffer." - (when (derived-mode-p 'Custom-mode) - (cons 'symbol ; gets refined to variable or face when acted upon - (save-excursion - (goto-char (point-min)) - (let (symbols) - (while (not (eobp)) - (when-let (widget (widget-at (point))) - (when (eq (car widget) 'custom-visibility) - (push - `(,(symbol-name - (plist-get (cdr (plist-get (cdr widget) :parent)) - :value)) - ,(point) - . ,(progn - (re-search-forward ":" (line-end-position) 'noerror) - (point))) - symbols))) - (forward-line)) - (nreverse symbols)))))) - - -(defun embark-collect--target () - "Return the Embark Collect candidate at point. -This takes into account `embark-transformer-alist'." - (let ((embark-target-finders '(embark-target-collect-candidate))) - (car (embark--targets)))) - -(defun embark--action-command (action) - "Turn an ACTION into a command to perform the action. -Returns the name of the command." - (let ((name (intern (format "embark-action--%s" - (embark--command-name action))))) - (fset name (lambda (arg) - (interactive "P") - (when-let (target (embark-collect--target)) - (let ((prefix-arg arg)) - (embark--act action target))))) - (when (fboundp action) - (put name 'function-documentation (documentation action))) - name)) - -(defun embark--all-bindings (keymap &optional nested) - "Return an alist of all bindings in KEYMAP. -If NESTED is non-nil subkeymaps are not flattened." - (let (bindings maps) - (map-keymap - (lambda (key def) - (cond - ((keymapp def) - (if nested - (push (cons (vector key) def) maps) - (dolist (bind (embark--all-bindings def)) - (push (cons (vconcat (vector key) (car bind)) (cdr bind)) - maps)))) - (def (push (cons (vector key) def) bindings)))) - (keymap-canonicalize keymap)) - (nconc (nreverse bindings) (nreverse maps)))) - -(defun embark-collect--direct-action-map (type) - "Return a direct action keymap for targets of given TYPE." - (let* ((actions (embark--action-keymap type nil)) - (map (make-sparse-keymap))) - (set-keymap-parent map button-map) - (pcase-dolist (`(,key . ,cmd) (embark--all-bindings actions)) - (unless (or (equal key [13]) - (memq cmd '(digit-argument negative-argument))) - (define-key map key (if (eq cmd 'embark-keymap-help) - #'embark-bindings-at-point - (embark--action-command cmd))))) - map)) - -(define-minor-mode embark-collect-direct-action-minor-mode - "Bind type-specific actions directly (without need for `embark-act')." - :init-value nil - :lighter " Act" - (unless (derived-mode-p 'embark-collect-mode) - (user-error "Not in an Embark Collect buffer")) - (save-excursion - (goto-char (point-min)) - (let ((inhibit-read-only t) maps) - (while (progn - (when (tabulated-list-get-id) - (put-text-property - (point) (button-end (point)) 'keymap - (if embark-collect-direct-action-minor-mode - (when-let ((target (embark-collect--target)) - (type (plist-get target :type))) - (or (alist-get type maps) - (setf (alist-get type maps) - (embark-collect--direct-action-map type))))))) - (forward-button 1 nil nil t)))))) - -(define-button-type 'embark-collect-entry - 'face 'embark-collect-candidate - 'action 'embark-collect-choose) - -(declare-function outline-toggle-children "outline") -(define-button-type 'embark-collect-group - 'face 'embark-collect-group-title - 'action (lambda (_) (outline-toggle-children))) - -(defun embark--boundaries () - "Get current minibuffer completion boundaries." - (let ((contents (minibuffer-contents)) - (pt (embark--minibuffer-point))) - (completion-boundaries - (substring contents 0 pt) - minibuffer-completion-table - minibuffer-completion-predicate - (substring contents pt)))) - -(defun embark-collect-choose (entry) - "Run default action on Embark Collect ENTRY." - (pcase-let ((`(,type ,text ,start . ,end) - (save-excursion - (goto-char entry) - (embark-target-collect-candidate)))) - (embark--act (embark--default-action type) - (list :target text - :type type - :bounds (cons start end))))) - -(defvar-keymap embark-collect-mode-map - :doc "Keymap for Embark collect mode." - :parent tabulated-list-mode-map - "a" #'embark-act - "A" #'embark-act-all - "M-a" #'embark-collect-direct-action-minor-mode - "E" #'embark-export - "s" #'isearch-forward - "n" #'forward-button - "p" #'backward-button - "}" 'outline-next-heading - "{" 'outline-previous-heading - "<remap> <forward-paragraph>" 'outline-next-heading - "<remap> <backward-paragraph>" 'outline-previous-heading - "<remap> <revert-buffer>" #'embark-rerun-collect-or-export) - -(defconst embark-collect--outline-string (string #x210000) - "Special string used for outline headings in Embark Collect buffers. -Chosen to be extremely unlikely to appear in a candidate.") - -(define-derived-mode embark-collect-mode tabulated-list-mode "Embark Collect" - "List of candidates to be acted on. -The command `embark-act' is bound `embark-collect-mode-map', but -you might prefer to change the key binding to match your other -key binding for it. Or alternatively you might want to enable the -embark collect direct action minor mode by adding the function -`embark-collect-direct-action-minor-mode' to -`embark-collect-mode-hook'. - -Reverting an Embark Collect buffer has slightly unusual behavior -if the buffer was obtained by running `embark-collect' from -within a minibuffer completion session. In that case reverting -just restarts the completion session, that is, the command that -opened the minibuffer is run again and the minibuffer contents -restored. You can then interact normally with the command, -perhaps editing the minibuffer contents, and, if you wish, you -can rerun `embark-collect' to get an updated buffer." - :interactive nil :abbrev-table nil :syntax-table nil) - -(defun embark-collect--metadatum (type metadatum) - "Get METADATUM for current buffer's candidates. -For non-minibuffers, assume candidates are of given TYPE." - (if (minibufferp) - (or (completion-metadata-get (embark--metadata) metadatum) - (plist-get completion-extra-properties - (intern (format ":%s" metadatum)))) - ;; otherwise fake some metadata for Marginalia users's benefit - (completion-metadata-get `((category . ,type)) metadatum))) - -(defun embark-collect--affixator (type) - "Get affixation function for current buffer's candidates. -For non-minibuffers, assume candidates are of given TYPE." - (or (embark-collect--metadatum type 'affixation-function) - (let ((annotator - (or (embark-collect--metadatum type 'annotation-function) - (lambda (_) "")))) - (lambda (candidates) - (mapcar (lambda (c) - (if-let (a (funcall annotator c)) (list c "" a) c)) - candidates))))) - -(defun embark--display-string (str) - ;; Note: Keep in sync with vertico--display-string - "Return display STR without display and invisible properties." - (let ((end (length str)) (pos 0) chunks) - (while (< pos end) - (let ((nextd (next-single-property-change pos 'display str end)) - (disp (get-text-property pos 'display str))) - (if (stringp disp) - (let ((face (get-text-property pos 'face str))) - (when face - (add-face-text-property - 0 (length disp) face t (setq disp (concat disp)))) - (setq pos nextd chunks (cons disp chunks))) - (while (< pos nextd) - (let ((nexti - (next-single-property-change pos 'invisible str nextd))) - (unless (or (get-text-property pos 'invisible str) - (and (= pos 0) (= nexti end))) ;; full=>no allocation - (push (substring str pos nexti) chunks)) - (setq pos nexti)))))) - (if chunks (apply #'concat (nreverse chunks)) str))) - -(defconst embark--hline - (propertize - (concat "\n" (propertize - " " 'display '(space :align-to right) - 'face '(:inherit completions-group-separator :height 0.01) - 'cursor-intangible t 'intangible t))) - "Horizontal line used to separate multiline collect entries.") - -(defun embark-collect--format-entries (candidates grouper) - "Format CANDIDATES for `tabulated-list-mode' grouped by GROUPER. -The GROUPER is either nil or a function like the `group-function' -completion metadatum, that is, a function of two arguments, the -first of which is a candidate and the second controls what is -computed: if nil, the title of the group the candidate belongs -to, and if non-nil, a rewriting of the candidate (useful to -simplify the candidate so it doesn't repeat the group title, for -example)." - (let ((max-width 0) - (transform - (if grouper (lambda (cand) (funcall grouper cand t)) #'identity))) - (setq - tabulated-list-entries - (mapcan - (lambda (group) - (let ((multiline (seq-some (lambda (x) (string-match-p "\n" (car x))) - candidates))) - (cons - `(nil [(,(concat (propertize embark-collect--outline-string - 'invisible t) - (format embark-collect-group-format (car group))) - type embark-collect-group) - ("" skip t)]) - (mapcar - (pcase-lambda (`(,cand ,prefix ,annotation)) - (let* ((display (embark--display-string (funcall transform cand))) - (length (length annotation)) - (faces (text-property-not-all - 0 length 'face nil annotation))) - (setq max-width (max max-width (+ (string-width prefix) - (string-width display)))) - (when faces - (add-face-text-property 0 length 'default t annotation)) - `(,cand - [(,(propertize - (if multiline (concat display embark--hline) display) - 'line-prefix prefix) - type embark-collect-entry) - (,annotation - skip t - ,@(unless faces - '(face embark-collect-annotation)))]))) - (cdr group))))) - (if grouper - (seq-group-by (lambda (item) (funcall grouper (car item) nil)) - candidates) - (list (cons "" candidates))))) - (if (null grouper) - (pop tabulated-list-entries) - (setq-local outline-regexp embark-collect--outline-string) - (outline-minor-mode)) - (setq tabulated-list-format - `[("Candidate" ,max-width t) ("Annotation" 0 t)]))) - -(defun embark-collect--update-candidates (buffer) - "Update candidates for Embark Collect BUFFER." - (let* ((transformed (embark--maybe-transform-candidates)) - (type (plist-get transformed :orig-type)) ; we need the originals for - (candidates (plist-get transformed :orig-candidates)) ; default action - (bounds (plist-get transformed :bounds)) - (affixator (embark-collect--affixator type)) - (grouper (embark-collect--metadatum type 'group-function))) - (when (eq type 'file) - (let ((dir (buffer-local-value 'default-directory buffer))) - (setq candidates - (mapcar (lambda (cand) - (let ((rel (file-relative-name cand dir))) - (if (string-prefix-p "../" rel) cand rel))) - candidates)))) - (if (seq-some #'identity bounds) - (cl-loop for cand in candidates and (start . _end) in bounds - when start - do (add-text-properties - 0 1 `(embark--location ,(copy-marker start)) cand))) - (setq candidates (funcall affixator candidates)) - (with-current-buffer buffer - (setq embark--type type) - (unless embark--command - (setq embark--command #'embark--goto)) - (embark-collect--format-entries candidates grouper)) - candidates)) - -(defun embark--goto (target) - "Jump to the original location of TARGET. -This function is used as a default action in Embark Collect -buffers when the candidates were a selection from a regular -buffer." - ;; TODO: ensure the location jumped to is visible - ;; TODO: remove duplication with embark-org-goto-heading - (when-let ((marker (get-text-property 0 'embark--location target))) - (pop-to-buffer (marker-buffer marker)) - (widen) - (goto-char marker) - (pulse-momentary-highlight-one-line))) - -(defun embark--collect (buffer-name) - "Create an Embark Collect buffer named BUFFER-NAME. - -The function `generate-new-buffer-name' is used to ensure the -buffer has a unique name." - (let ((buffer (generate-new-buffer buffer-name)) - (rerun (embark--rerun-function #'embark-collect))) - (with-current-buffer buffer - ;; we'll run the mode hooks once the buffer is displayed, so - ;; the hooks can make use of the window - (delay-mode-hooks (embark-collect-mode))) - - (embark--cache-info buffer) - (unless (embark-collect--update-candidates buffer) - (user-error "No candidates to collect")) - - (with-current-buffer buffer - (setq tabulated-list-use-header-line nil ; default to no header - header-line-format nil - tabulated-list--header-string nil) - (setq embark--rerun-function rerun)) - - (let ((window (display-buffer buffer))) - (with-selected-window window - (run-mode-hooks) - (tabulated-list-revert)) - (set-window-dedicated-p window t) - buffer))) - -(defun embark--descriptive-buffer-name (type) - "Return a descriptive name for an Embark collect or export buffer. -TYPE should be either `collect' or `export'." - (format "*Embark %s: %s*" - (capitalize (symbol-name type)) - (if (minibufferp) - (format "%s - %s" embark--command - (minibuffer-contents-no-properties)) - (buffer-name)))) - -;;;###autoload -(defun embark-collect () - "Create an Embark Collect buffer. - -To control the display, add an entry to `display-buffer-alist' -with key \"Embark Collect\". - -In Embark Collect buffers `revert-buffer' is remapped to -`embark-rerun-collect-or-export', which has slightly unusual -behavior if the buffer was obtained by running `embark-collect' -from within a minibuffer completion session. In that case -rerunning just restarts the completion session, that is, the -command that opened the minibuffer is run again and the -minibuffer contents restored. You can then interact normally with -the command, perhaps editing the minibuffer contents, and, if you -wish, you can rerun `embark-collect' to get an updated buffer." - (interactive) - (let ((buffer (embark--collect (embark--descriptive-buffer-name 'collect)))) - (when (minibufferp) - (embark--run-after-command #'pop-to-buffer buffer) - (embark--quit-and-run #'message nil)))) - -;;;###autoload -(defun embark-live () - "Create a live-updating Embark Collect buffer. - -To control the display, add an entry to `display-buffer-alist' -with key \"Embark Live\"." - (interactive) - (let ((live-buffer (embark--collect - (format "*Embark Live: %s*" - (if (minibufferp) - (format "M-x %s" embark--command) - (buffer-name))))) - (run-collect (make-symbol "run-collect")) - (stop-collect (make-symbol "stop-collect")) - timer) - (setf (symbol-function stop-collect) - (lambda () - (remove-hook 'change-major-mode-hook stop-collect t) - (remove-hook 'after-change-functions run-collect t))) - (setf (symbol-function run-collect) - (lambda (_1 _2 _3) - (unless timer - (setq timer - (run-with-idle-timer - 0.05 nil - (lambda () - (if (not (buffer-live-p live-buffer)) - (funcall stop-collect) - (embark-collect--update-candidates live-buffer) - (with-current-buffer live-buffer - ;; TODO figure out why I can't restore point - (tabulated-list-print t t)) - (setq timer nil)))))))) - (add-hook 'after-change-functions run-collect nil t) - (when (minibufferp) - (add-hook 'change-major-mode-hook stop-collect nil t)))) - -(defun embark--rerun-function (kind) - "Return a rerun function for an export or collect buffer in this context. -The parameter KIND should be either `embark-export' or `embark-collect'." - (let ((buffer (or embark--target-buffer (embark--target-buffer))) - (command embark--command)) - (cl-flet ((rerunner (action) - (lambda (&rest _) - (quit-window 'kill-buffer) - (with-current-buffer - (if (buffer-live-p buffer) buffer (current-buffer)) - (let ((embark--command command)) - (funcall action)))))) - (if (minibufferp) - (rerunner - (let ((input (minibuffer-contents-no-properties))) - (lambda () - (minibuffer-with-setup-hook - (lambda () - (delete-minibuffer-contents) - (insert input)) - (setq this-command embark--command) - (command-execute embark--command))))) - (rerunner kind))))) - -(defun embark-rerun-collect-or-export () - "Rerun the `embark-collect' or `embark-export' that created this buffer." - (interactive) - (if embark--rerun-function - (funcall embark--rerun-function) - (user-error "No function to rerun collect or export found"))) - -;;;###autoload -(defun embark-export () - "Create a type-specific buffer to manage current candidates. -The variable `embark-exporters-alist' controls how to make the -buffer for each type of completion. - -In Embark Export buffers `revert-buffer' is remapped to -`embark-rerun-collect-or-export', which has slightly unusual -behavior if the buffer was obtained by running `embark-export' -from within a minibuffer completion session. In that case -reverting just restarts the completion session, that is, the -command that opened the minibuffer is run again and the -minibuffer contents restored. You can then interact normally -with the command, perhaps editing the minibuffer contents, and, -if you wish, you can rerun `embark-export' to get an updated -buffer." - (interactive) - (let* ((transformed (embark--maybe-transform-candidates)) - (candidates (or (plist-get transformed :candidates) - (user-error "No candidates for export"))) - (type (plist-get transformed :type))) - (let ((exporter (or (alist-get type embark-exporters-alist) - (alist-get t embark-exporters-alist)))) - (if (eq exporter 'embark-collect) - (embark-collect) - (let* ((after embark-after-export-hook) - (cmd embark--command) - (name (embark--descriptive-buffer-name 'export)) - (rerun (embark--rerun-function #'embark-export)) - (buffer (save-excursion - (funcall exporter candidates) - (rename-buffer name t) - (current-buffer)))) - (embark--quit-and-run - (lambda () - (pop-to-buffer buffer) - (setq embark--rerun-function rerun) - (use-local-map - (make-composed-keymap - '(keymap - (remap keymap - (revert-buffer . embark-rerun-collect-or-export))) - (current-local-map))) - (let ((embark-after-export-hook after) - (embark--command cmd)) - (run-hooks 'embark-after-export-hook))))))))) - -(defmacro embark--export-rename (buffer title &rest body) - "Run BODY and rename BUFFER to Embark export buffer with TITLE." - (declare (indent 2)) - (let ((saved (make-symbol "saved"))) - `(let ((,saved (embark-rename-buffer - ,buffer " *Embark Saved*" t))) - ,@body - (set-buffer (embark-rename-buffer - ,buffer ,(format "*Embark Export %s*" title) t)) - (when ,saved (embark-rename-buffer ,saved ,buffer))))) - -(defun embark--export-customize (items type pred) - "Create a customization buffer listing ITEMS. -TYPE is the items type. -PRED is a predicate function used to filter the items." - (custom-buffer-create - (cl-loop for item in items - for sym = (intern-soft item) - when (and sym (funcall pred sym)) collect `(,sym ,type)))) - -(autoload 'apropos-parse-pattern "apropos") -(autoload 'apropos-symbols-internal "apropos") -(defun embark-export-apropos (symbols) - "Create apropos buffer listing SYMBOLS." - (embark--export-rename "*Apropos*" "Apropos" - (apropos-parse-pattern "") ;; Initialize apropos pattern - ;; HACK: Ensure that order of exported symbols is kept. - (cl-letf (((symbol-function #'sort) (lambda (list _pred) (nreverse list)))) - (apropos-symbols-internal - (delq nil (mapcar #'intern-soft symbols)) - (bound-and-true-p apropos-do-all))))) - -(defun embark-export-customize-face (faces) - "Create a customization buffer listing FACES." - (embark--export-customize faces 'custom-face #'facep)) - -(defun embark-export-customize-variable (variables) - "Create a customization buffer listing VARIABLES." - ;; The widget library serializes/deserializes the values. We advise - ;; the serialization in order to avoid errors for nonserializable - ;; variables. - (cl-letf* ((ht (make-hash-table :test #'equal)) - (orig-read (symbol-function #'read)) - (orig-write (symbol-function 'widget-sexp-value-to-internal)) - ((symbol-function #'read) - (lambda (&optional str) - (condition-case nil - (funcall orig-read str) - (error (gethash str ht))))) - ((symbol-function 'widget-sexp-value-to-internal) - (lambda (widget val) - (let ((str (funcall orig-write widget val))) - (puthash str val ht) - str)))) - (embark--export-customize variables 'custom-variable #'boundp))) - -(defun embark-export-ibuffer (buffers) - "Create an ibuffer buffer listing BUFFERS." - (ibuffer t "*Embark Export Ibuffer*" - `((predicate . (member (buffer-name) ',buffers))))) - -(autoload 'dired-check-switches "dired") -(declare-function dired-unadvertise "dired") -(defvar dired-directory) - -(defun embark-export-dired (files) - "Create a Dired buffer listing FILES." - (setq files (mapcar #'directory-file-name - (cl-remove-if-not #'file-exists-p files))) - (when (dired-check-switches dired-listing-switches "A" "almost-all") - (setq files (cl-remove-if - (lambda (path) - (let ((file (file-name-nondirectory path))) - (or (string= file ".") (string= file "..")))) - files))) - (cl-letf* ((dir (or (file-name-directory (try-completion "" files)) "")) - ;; Prevent reusing existing Dired buffer. - ((symbol-function 'dired-find-buffer-nocreate) #'ignore) - (buf (dired-noselect - (cons (expand-file-name dir) - (mapcar (lambda (file) (string-remove-prefix dir file)) - files))))) - (with-current-buffer buf - ;; Unadvertise to prevent the new buffer from being reused. - (dired-unadvertise default-directory) - (rename-buffer (format "*Embark Export Dired %s*" default-directory))) - (pop-to-buffer buf))) - -(autoload 'package-menu-mode "package") -(autoload 'package-menu--generate "package") - -(defun embark-export-list-packages (packages) - "Create a package menu mode buffer listing PACKAGES." - (let ((buf (generate-new-buffer "*Embark Export Packages*"))) - (with-current-buffer buf - (package-menu-mode) - (package-menu--generate nil (mapcar #'intern packages))) - (pop-to-buffer buf))) - -(defvar bookmark-alist) - -(defun embark-export-bookmarks (bookmarks) - "Create a `bookmark-bmenu-mode' buffer listing BOOKMARKS." - (embark--export-rename "*Bookmark List*" "Bookmarks" - (let ((bookmark-alist - (cl-remove-if-not - (lambda (bmark) - (member (car bmark) bookmarks)) - bookmark-alist))) - (bookmark-bmenu-list)))) - -;;; Multiple target selection - -(defface embark-selected '((t (:inherit match))) - "Face for selected candidates.") - -(defcustom embark-selection-indicator - #(" Embark:%s " 1 12 (face (embark-selected bold))) - "Mode line indicator used for selected candidates." - :type '(choice string (const nil))) - -(defvar-local embark--selection nil - "Buffer local list of selected targets. -Add or remove elements to this list using the `embark-select' -action.") - -(defun embark--selection-indicator () - "Mode line indicator showing number of selected items." - (when-let ((sel - (buffer-local-value - 'embark--selection - (or (when-let ((win (active-minibuffer-window))) - (window-buffer win)) - (current-buffer))))) - (format embark-selection-indicator (length sel)))) - -(cl-defun embark--select - (&key orig-target orig-type bounds &allow-other-keys) - "Add or remove ORIG-TARGET of given ORIG-TYPE to the selection. -If BOUNDS are given, also highlight the target when selecting it." - (cl-flet ((multi-type (x) (car (get-text-property 0 'multi-category x)))) - (if-let* ((existing (seq-find - (pcase-lambda (`(,cand . ,ov)) - (and - (equal cand orig-target) - (if (and bounds ov) - (and (= (car bounds) (overlay-start ov)) - (= (cdr bounds) (overlay-end ov))) - (let ((cand-type (multi-type cand))) - (or (eq cand-type orig-type) - (eq cand-type (multi-type orig-target))))))) - embark--selection))) - (progn - (when (cdr existing) (delete-overlay (cdr existing))) - (setq embark--selection (delq existing embark--selection))) - (let ((target (copy-sequence orig-target)) overlay) - (when bounds - (setq overlay (make-overlay (car bounds) (cdr bounds))) - (overlay-put overlay 'category 'embark-selected-overlay)) - (add-text-properties 0 (length orig-target) - `(multi-category ,(cons orig-type orig-target)) - target) - (push (cons target overlay) embark--selection)))) - (when embark-selection-indicator - (add-to-list 'mode-line-misc-info '(:eval (embark--selection-indicator))) - (force-mode-line-update t))) - -;;;###autoload -(defun embark-select () - "Add or remove the target from the current buffer's selection. -You can act on all selected targets at once with `embark-act-all'. -When called from outside `embark-act' this command will select -the first target at point." - (interactive) - (if-let ((target (car (embark--targets)))) - (apply #'embark--select target) - (user-error "No target to select"))) - -(defun embark-selected-candidates () - "Return currently selected candidates in the buffer." - (when embark--selection - (cl-flet ((unwrap (x) (get-text-property 0 'multi-category x))) - (let* ((first-type (car (unwrap (caar embark--selection)))) - (same (cl-every (lambda (item) - (eq (car (unwrap (car item))) first-type)) - embark--selection)) - (extract (if same - (pcase-lambda (`(,cand . ,overlay)) - (cons (cdr (unwrap cand)) overlay)) - #'identity))) - (cons - (if same first-type 'multi-category) - (nreverse - (mapcar - (lambda (item) - (pcase-let ((`(,cand . ,ov) (funcall extract item))) - (if ov `(,cand ,(overlay-start ov) . ,(overlay-end ov)) cand))) - embark--selection))))))) - -;;; Integration with external packages, mostly completion UIs - -;; marginalia - -;; Ensure that the Marginalia cache is reset, such that -;; `embark-toggle-variable-value' updates the display (See #540). -(with-eval-after-load 'marginalia - (push 'marginalia--cache-reset (alist-get :always embark-post-action-hooks))) - -;; vertico - -(declare-function vertico--candidate "ext:vertico") -(declare-function vertico--update "ext:vertico") -(defvar vertico--input) -(defvar vertico--candidates) -(defvar vertico--base) - -(defun embark--vertico-selected () - "Target the currently selected item in Vertico. -Return the category metadatum as the type of the target." - (when vertico--input - ;; Force candidate computation, if candidates are not yet available. - (vertico--update) - (cons (completion-metadata-get (embark--metadata) 'category) - (vertico--candidate)))) - -(defun embark--vertico-candidates () - "Collect the current Vertico candidates. -Return the category metadatum as the type of the candidates." - (when vertico--input - ;; Force candidate computation, if candidates are not yet available. - (vertico--update) - (cons (completion-metadata-get (embark--metadata) 'category) - vertico--candidates))) - -(defun embark--vertico-indicator () - "Embark indicator highlighting the current Vertico candidate." - (let ((fr face-remapping-alist)) - (lambda (&optional keymap _targets _prefix) - (when vertico--input - (setq-local face-remapping-alist - (if keymap - (cons '(vertico-current . embark-target) fr) - fr)))))) - -(with-eval-after-load 'vertico - (cl-defmethod vertico--format-candidate - :around (cand prefix suffix index start &context (embark--selection cons)) - (when (cl-find (concat vertico--base (nth index vertico--candidates)) - embark--selection - :test #'equal :key #'car) - (setq cand (copy-sequence cand)) - (add-face-text-property 0 (length cand) 'embark-selected t cand)) - (cl-call-next-method cand prefix suffix index start)) - (add-hook 'embark-indicators #'embark--vertico-indicator) - (add-hook 'embark-target-finders #'embark--vertico-selected) - (add-hook 'embark-candidate-collectors #'embark--vertico-candidates) - (remove-hook 'embark-candidate-collectors #'embark-selected-candidates) - (add-hook 'embark-candidate-collectors #'embark-selected-candidates)) - -;; ivy - -(declare-function ivy--expand-file-name "ext:ivy") -(declare-function ivy-state-current "ext:ivy") -(defvar ivy-text) -(defvar ivy-last) -(defvar ivy--old-cands) ; this stores the current candidates :) -(defvar ivy--length) - -(defun embark--ivy-selected () - "Target the currently selected item in Ivy. -Return the category metadatum as the type of the target." - ;; my favorite way of detecting Ivy - (when (memq 'ivy--queue-exhibit post-command-hook) - (cons - (completion-metadata-get (embark--metadata) 'category) - (ivy--expand-file-name - (if (and (> ivy--length 0) - (stringp (ivy-state-current ivy-last))) - (ivy-state-current ivy-last) - ivy-text))))) - -(defun embark--ivy-candidates () - "Return all current Ivy candidates." - ;; my favorite way of detecting Ivy - (when (memq 'ivy--queue-exhibit post-command-hook) - (cons - ;; swiper-isearch uses swiper-isearch-function as a completion - ;; table, but it doesn't understand metadata queries - (ignore-errors - (completion-metadata-get (embark--metadata) 'category)) - ivy--old-cands))) - -(with-eval-after-load 'ivy - (add-hook 'embark-target-finders #'embark--ivy-selected) - (add-hook 'embark-candidate-collectors #'embark--ivy-candidates) - (remove-hook 'embark-candidate-collectors #'embark-selected-candidates) - (add-hook 'embark-candidate-collectors #'embark-selected-candidates)) - -;;; Custom actions - -(defvar embark-separator-history nil - "Input history for the separators used by some embark commands. -The commands that prompt for a string separator are -`embark-insert' and `embark-copy-as-kill'.") - -(defun embark-keymap-help () - "Prompt for an action to perform or command to become and run it." - (interactive) - (user-error "Not meant to be called directly")) - -(defun embark-toggle-quit () - "Toggle whether the following action quits the minibuffer." - (interactive) - (when (minibufferp) - (setq embark--toggle-quit (not embark--toggle-quit)) - (if (consp embark-quit-after-action) - (message "Will %sobey embark-quit-after-action." - (if embark--toggle-quit "dis" "")) - (message - "Will %squit minibuffer after action" - (if (eq embark--toggle-quit embark-quit-after-action) "not " ""))))) - -(defun embark--separator (strings) - "Return a separator to join the STRINGS together. -With a prefix argument, prompt the user (unless STRINGS has 0 or -1 elements, in which case a separator is not needed)." - (if (and current-prefix-arg (cdr strings)) - (read-string "Separator: " nil 'embark-separator-history) - "\n")) - -(defun embark-copy-as-kill (strings) - "Join STRINGS and save on the `kill-ring'. -With a prefix argument, prompt for the separator to join the -STRINGS, which defaults to a newline." - (kill-new (string-join strings (embark--separator strings)))) - -(defun embark-insert (strings) - "Join STRINGS and insert the result at point. -With a prefix argument, prompt for the separator to join the -STRINGS, which defaults to a newline. - -Some whitespace is also inserted if necessary to avoid having the -inserted string blend into the existing buffer text. More -precisely: - -1. If the inserted string does not contain newlines, a space may -be added before or after it as needed to avoid inserting a word -constituent character next to an existing word constituent. - -2. For a multiline inserted string, newlines may be added before -or after as needed to ensure the inserted string is on lines of -its own." - (let* ((separator (embark--separator strings)) - (multiline - (or (and (cdr strings) (string-match-p "\n" separator)) - (and (null (cdr strings)) - (equal (buffer-substring (line-beginning-position) - (line-end-position)) - (car strings))) - (seq-some (lambda (s) (string-match-p "\n" s)) strings)))) - (cl-labels ((maybe-space () - (and (looking-at "\\w") (looking-back "\\w" 1) - (insert " "))) - (maybe-newline () - (or (looking-back "^[ \t]*" 40) (looking-at "\n") - (newline-and-indent))) - (maybe-whitespace () - (if multiline (maybe-newline) (maybe-space))) - (ins-string () - (let ((start (point))) - (insert - (mapconcat #'substring-no-properties strings separator)) - (save-excursion (goto-char start) (maybe-whitespace)) - (when (looking-back "\n" 1) (delete-char -1)) - (save-excursion (maybe-whitespace))))) - (if buffer-read-only - (with-selected-window (other-window-for-scrolling) - (ins-string)) - (ins-string))))) - -;; For Emacs 28 dired-jump will be moved to dired.el, but it seems -;; that since it already has an autoload in Emacs 28, this next -;; autoload is ignored. -(autoload 'dired-jump "dired-x" nil t) - -(defun embark-dired-jump (file &optional other-window) - "Open Dired buffer in directory containing FILE and move to its line. -When called with a prefix argument OTHER-WINDOW, open Dired in other window." - (interactive "fJump to Dired file: \nP") - (dired-jump other-window file)) - -(defun embark--read-from-history (prompt candidates &optional category) - "Read with completion from list of history CANDIDATES of CATEGORY. -Sorting and history are disabled. PROMPT is the prompt message." - (completing-read prompt - (embark--with-category category candidates) - nil t nil t)) - -(defun embark-kill-ring-remove (text) - "Remove TEXT from `kill-ring'." - (interactive (list (embark--read-from-history - "Remove from kill-ring: " kill-ring 'kill-ring))) - (embark-history-remove text) - (setq kill-ring (delete text kill-ring))) - -(defvar recentf-list) -(defun embark-recentf-remove (file) - "Remove FILE from the list of recent files." - (interactive (list (embark--read-from-history - "Remove recent file: " recentf-list 'file))) - (embark-history-remove (expand-file-name file)) - (embark-history-remove (abbreviate-file-name file)) - (when (and (boundp 'recentf-list) (fboundp 'recentf-expand-file-name)) - (setq recentf-list (delete (recentf-expand-file-name file) recentf-list)))) - -(defun embark-history-remove (str) - "Remove STR from `minibuffer-history-variable'. -Many completion UIs sort by history position. This command can be used -to remove entries from the history, such that they are not sorted closer -to the top." - (interactive (list (embark--read-from-history - "Remove history item: " - (if (eq minibuffer-history-variable t) - (user-error "No minibuffer history") - (symbol-value minibuffer-history-variable))))) - (unless (eq minibuffer-history-variable t) - (set minibuffer-history-variable - (delete str (symbol-value minibuffer-history-variable))))) - -(defvar xref-backend-functions) - -(defun embark-find-definition (symbol) - "Find definition of Emacs Lisp SYMBOL." - (interactive "sSymbol: ") - (let ((xref-backend-functions (lambda () 'elisp))) - (xref-find-definitions symbol))) - -(defun embark-info-lookup-symbol (symbol) - "Display the definition of SYMBOL, from the Elisp manual." - (interactive "SSymbol: ") - (info-lookup-symbol symbol 'emacs-lisp-mode)) - -(defun embark-rename-buffer (buffer newname &optional unique) - "Rename BUFFER to NEWNAME, optionally making it UNIQUE. -Interactively, you can set UNIQUE with a prefix argument. -Returns the new name actually used." - (interactive "bBuffer: \nBRename %s to: \nP") - (when-let ((buf (get-buffer buffer))) - (with-current-buffer buf - (rename-buffer newname unique)))) - -(defun embark--package-url (pkg) - "Return homepage for package PKG." - (when-let (desc (embark--package-desc pkg)) - (alist-get :url (package-desc-extras desc)))) - -(defun embark--prompt-for-package () - "Prompt user for a package name." - ;; this code is taken from the interactive spec of describe-package - (unless package--initialized - (package-initialize t)) - (intern - (completing-read "Package: " - (append (mapcar #'car package-alist) - (mapcar #'car package-archive-contents) - (mapcar #'car package--builtins))))) - -(defun embark-browse-package-url (pkg) - "Open homepage for package PKG with `browse-url'." - (interactive (list (embark--prompt-for-package))) - (if-let ((url (embark--package-url pkg))) - (browse-url url) - (user-error "No homepage found for `%s'" pkg))) - -(defun embark-save-package-url (pkg) - "Save URL of homepage for package PKG on the `kill-ring'." - (interactive (list (embark--prompt-for-package))) - (if-let ((url (embark--package-url pkg))) - (kill-new url) - (user-error "No homepage found for `%s'" pkg))) - -(defun embark-save-variable-value (var) - "Save value of VAR in the `kill-ring'." - (interactive "SVariable: ") - (kill-new (string-trim (pp-to-string (symbol-value var))))) - -(defun embark-insert-variable-value (var) - "Insert value of VAR." - (interactive "SVariable: ") - (embark-insert (list (string-trim (pp-to-string (symbol-value var)))))) - -(defun embark-toggle-variable (var &optional local) - "Toggle value of boolean variable VAR. -If prefix LOCAL is non-nil make variable local." - (interactive "SVariable: \nP") - (let ((val (symbol-value var))) - (unless (memq val '(nil t)) - (user-error "Not a boolean variable")) - (when local - (make-local-variable var)) - (funcall (or (get var 'custom-set) 'set) var (not val)))) - -(defun embark-insert-relative-path (file) - "Insert relative path to FILE. -The insert path is relative to `default-directory'." - (interactive "FFile: ") - (embark-insert (list (file-relative-name (substitute-in-file-name file))))) - -(defun embark-save-relative-path (file) - "Save the relative path to FILE in the kill ring. -The insert path is relative to `default-directory'." - (interactive "FFile: ") - (kill-new (file-relative-name (substitute-in-file-name file)))) - -(defun embark-shell-command-on-buffer (buffer command &optional replace) - "Run shell COMMAND on contents of BUFFER. -Called with \\[universal-argument], replace contents of buffer -with command output. For replacement behavior see -`shell-command-dont-erase-buffer' setting." - (interactive - (list - (read-buffer "Buffer: " nil t) - (read-shell-command "Shell command: ") - current-prefix-arg)) - (with-current-buffer buffer - (shell-command-on-region (point-min) (point-max) - command - (and replace (current-buffer))))) - -(defun embark-open-externally (file) - "Open FILE or url using system's default application." - (interactive "sOpen externally: ") - (unless (string-match-p "\\`[a-z]+://" file) - (setq file (expand-file-name file))) - (message "Opening `%s' externally..." file) - (if (and (eq system-type 'windows-nt) - (fboundp 'w32-shell-execute)) - (w32-shell-execute "open" file) - (call-process (pcase system-type - ('darwin "open") - ('cygwin "cygstart") - (_ "xdg-open")) - nil 0 nil file))) - -(declare-function bookmark-prop-get "bookmark") -(declare-function bookmark-completing-read "bookmark") - -(defun embark-bookmark-open-externally (bookmark) - "Open BOOKMARK in external application." - (interactive (list (bookmark-completing-read "Open externally: "))) - (embark-open-externally - (or (bookmark-prop-get bookmark 'location) - (bookmark-prop-get bookmark 'filename) - (user-error "Bookmark `%s' does not have a location" bookmark)))) - -(defun embark-bury-buffer (buf) - "Bury buffer BUF." - (interactive "bBuffer: ") - (if-let (win (get-buffer-window buf)) - (with-selected-window win - (bury-buffer)) - (bury-buffer))) - -(defun embark-kill-buffer-and-window (buf) - "Kill buffer BUF and delete its window." - (interactive "bBuffer: ") - (when-let (buf (get-buffer buf)) - (if-let (win (get-buffer-window buf)) - (with-selected-window win - (kill-buffer-and-window)) - (kill-buffer buf)))) - -(defun embark-save-unicode-character (char) - "Save Unicode character CHAR to kill ring." - (interactive - (list (read-char-by-name "Insert character (Unicode name or hex): "))) - (kill-new (format "%c" char))) - -(defun embark-isearch-forward () - "Prompt for string in the minibuffer and start isearch forwards. -Unlike isearch, this command reads the string from the -minibuffer, which means it can be used as an Embark action." - (interactive) - (isearch-mode t) - (isearch-edit-string)) - -(defun embark-isearch-backward () - "Prompt for string in the minibuffer and start isearch backwards. -Unlike isearch, this command reads the string from the -minibuffer, which means it can be used as an Embark action." - (interactive) - (isearch-mode nil) - (isearch-edit-string)) - -(defun embark-toggle-highlight () - "Toggle symbol highlighting using `highlight-symbol-at-point'." - (interactive) - (let ((regexp (find-tag-default-as-symbol-regexp)) - (highlighted (cl-find-if #'boundp - '(hi-lock-interactive-lighters - hi-lock-interactive-patterns)))) - (if (and highlighted (assoc regexp (symbol-value highlighted))) - (unhighlight-regexp regexp) - (highlight-symbol-at-point)))) - -(defun embark-next-symbol () - "Jump to next occurrence of symbol at point. -The search respects symbol boundaries." - (interactive) - (if-let ((symbol (thing-at-point 'symbol))) - (let ((regexp (format "\\_<%s\\_>" (regexp-quote symbol)))) - (when (looking-at regexp) - (forward-symbol 1)) - (unless (re-search-forward regexp nil t) - (user-error "Symbol `%s' not found" symbol))) - (user-error "No symbol at point"))) - -(defun embark-previous-symbol () - "Jump to previous occurrence of symbol at point. -The search respects symbol boundaries." - (interactive) - (if-let ((symbol (thing-at-point 'symbol))) - (let ((regexp (format "\\_<%s\\_>" (regexp-quote symbol)))) - (when (looking-back regexp (- (point) (length symbol))) - (forward-symbol -1)) - (unless (re-search-backward regexp nil t) - (user-error "Symbol `%s' not found" symbol))) - (user-error "No symbol at point"))) - -(defun embark-compose-mail (address) - "Compose email to ADDRESS." - ;; The only reason we cannot use compose-mail directly is its - ;; interactive specification, which just supplies nil for the - ;; address (and several other arguments). - (interactive "sTo: ") - (compose-mail address)) - -(autoload 'pp-display-expression "pp") - -(defun embark-pp-eval-defun (edebug) - "Run `eval-defun' and pretty print the result. -With a prefix argument EDEBUG, instrument the code for debugging." - (interactive "P") - (cl-letf (((symbol-function #'eval-expression-print-format) - (lambda (result) - (pp-display-expression result "*Pp Eval Output*")))) - (eval-defun edebug))) - -(defun embark-eval-replace (noquote) - "Evaluate region and replace with evaluated result. -If NOQUOTE is non-nil (interactively, if called with a prefix -argument), no quoting is used for strings." - (interactive "P") - (let ((beg (region-beginning)) - (end (region-end))) - (save-excursion - (goto-char end) - (insert (format (if noquote "%s" "%S") - (eval (read (buffer-substring beg end)) lexical-binding))) - (delete-region beg end)))) - -(when (< emacs-major-version 29) - (defun embark-elp-restore-package (prefix) - "Remove instrumentation from functions with names starting with PREFIX." - (interactive "SPrefix: ") - (when (fboundp 'elp-restore-list) - (elp-restore-list - (mapcar #'intern - (all-completions (symbol-name prefix) - obarray 'elp-profilable-p)))))) - -(defmacro embark--define-hash (algorithm) - "Define command which computes hash from a string. -ALGORITHM is the hash algorithm symbol understood by `secure-hash'." - `(defun ,(intern (format "embark-hash-%s" algorithm)) (str) - ,(format "Compute %s hash of STR and store it in the kill ring." algorithm) - (interactive "sString: ") - (let ((hash (secure-hash ',algorithm str))) - (kill-new hash) - (message "%s: %s" ',algorithm hash)))) - -(embark--define-hash md5) -(embark--define-hash sha1) -(embark--define-hash sha224) -(embark--define-hash sha256) -(embark--define-hash sha384) -(embark--define-hash sha512) - -(defun embark-encode-url (start end) - "Properly URI-encode the region between START and END in current buffer." - (interactive "r") - (let ((encoded (url-encode-url (buffer-substring-no-properties start end)))) - (delete-region start end) - (insert encoded))) - -(defun embark-decode-url (start end) - "Decode the URI-encoded region between START and END in current buffer." - (interactive "r") - (let ((decoded (url-unhex-string (buffer-substring-no-properties start end)))) - (delete-region start end) - (insert decoded))) - -(defvar epa-replace-original-text) -(defun embark-epa-decrypt-region (start end) - "Decrypt region between START and END." - (interactive "r") - (let ((epa-replace-original-text t)) - (epa-decrypt-region start end))) - -(defvar eww-download-directory) -(autoload 'eww-download-callback "eww") - -(defun embark-download-url (url) - "Download URL to `eww-download-directory'." - (interactive "sDownload URL: ") - (let ((dir eww-download-directory)) - (when (functionp dir) (setq dir (funcall dir))) - (access-file dir "Download failed") - (url-retrieve - url #'eww-download-callback - (if (>= emacs-major-version 28) (list url dir) (list url))))) - -;;; Setup and pre-action hooks - -(defun embark--restart (&rest _) - "Restart current command with current input. -Use this to refresh the list of candidates for commands that do -not handle that themselves." - (when (minibufferp) - (embark--become-command embark--command (minibuffer-contents)))) - -(defun embark--shell-prep (&rest _) - "Prepare target for use as argument for a shell command. -This quotes the spaces, inserts an extra space at the beginning -and leaves the point to the left of it." - (let ((contents (minibuffer-contents))) - (delete-minibuffer-contents) - (insert " " (shell-quote-wildcard-pattern contents)) - (goto-char (minibuffer-prompt-end)))) - -(defun embark--force-complete (&rest _) - "Select first minibuffer completion candidate matching target." - (minibuffer-force-complete)) - -(cl-defun embark--eval-prep (&key type &allow-other-keys) - "If target's TYPE is variable, skip edit; if function, wrap in ()." - (when (memq type '(command function)) - (embark--allow-edit) - (goto-char (minibuffer-prompt-end)) - (insert "(") - (goto-char (point-max)) - (insert ")") - (backward-char))) - -(cl-defun embark--beginning-of-target (&key bounds &allow-other-keys) - "Go to beginning of the target BOUNDS." - (when (number-or-marker-p (car bounds)) - (goto-char (car bounds)))) - -(cl-defun embark--end-of-target (&key bounds &allow-other-keys) - "Go to end of the target BOUNDS." - (when (number-or-marker-p (cdr bounds)) - (goto-char (cdr bounds)))) - -(cl-defun embark--mark-target (&rest rest &key run bounds &allow-other-keys) - "Mark the target if its BOUNDS are known. -After marking the target, call RUN with the REST of its arguments." - (cond - ((and bounds run) - (save-mark-and-excursion - (set-mark (cdr bounds)) - (goto-char (car bounds)) - (apply run :bounds bounds rest))) - (bounds ;; used as pre- or post-action hook - (set-mark (cdr bounds)) - (goto-char (car bounds))) - (run (apply run rest)))) - -(cl-defun embark--unmark-target (&rest _) - "Deactivate the region target." - (deactivate-mark t)) - -(cl-defun embark--narrow-to-target - (&rest rest &key run bounds &allow-other-keys) - "Narrow buffer to target if its BOUNDS are known. -Intended for use as an Embark around-action hook. This function -runs RUN with the buffer narrowed to given BOUNDS passing along -the REST of the arguments." - (if bounds - (save-excursion - (save-restriction - (narrow-to-region (car bounds) (cdr bounds)) - (goto-char (car bounds)) - (apply run :bounds bounds rest))) - (apply run rest))) - -(defun embark--allow-edit (&rest _) - "Allow editing the target." - (remove-hook 'post-command-hook #'exit-minibuffer t) - (remove-hook 'post-command-hook 'ivy-immediate-done t)) - -(defun embark--ignore-target (&rest _) - "Ignore the target." - (let ((contents - (get-text-property (minibuffer-prompt-end) 'embark--initial-input))) - (delete-minibuffer-contents) - (when contents (insert contents))) - (embark--allow-edit)) - -(autoload 'xref-push-marker-stack "xref") -(defun embark--xref-push-marker (&rest _) - "Push a marker onto the xref marker stack." - (xref-push-marker-stack)) - -(cl-defun embark--confirm (&key action target &allow-other-keys) - "Ask for confirmation before running the ACTION on the TARGET." - (unless (y-or-n-p (format "Run %s on %s? " action target)) - (user-error "Canceled"))) - -(defconst embark--associated-file-fn-alist - `((file . identity) - (buffer . ,(lambda (target) - (let ((buffer (get-buffer target))) - (or (buffer-file-name buffer) - (buffer-local-value 'default-directory buffer))))) - (bookmark . bookmark-location) - (library . locate-library)) - "Alist of functions that extract a file path from targets of a given type.") - -(defun embark--associated-directory (target type) - "Return directory associated to TARGET of given TYPE. -The supported values of TYPE are file, buffer, bookmark and -library, which have an obvious notion of associated directory." - (when-let ((file-fn (alist-get type embark--associated-file-fn-alist)) - (file (funcall file-fn target))) - (if (file-directory-p file) - (file-name-as-directory file) - (file-name-directory file)))) - -(cl-defun embark--cd (&rest rest &key run target type &allow-other-keys) - "Run action with `default-directory' set to the directory of TARGET. -The supported values of TYPE are file, buffer, bookmark and -library, which have an obvious notion of associated directory. -The REST of the arguments are also passed to RUN." - (let ((default-directory - (or (embark--associated-directory target type) default-directory))) - (apply run :target target :type type rest))) - -(cl-defun embark--save-excursion (&rest rest &key run &allow-other-keys) - "Run action without moving point. -This simply calls RUN with the REST of its arguments inside -`save-excursion'." - (save-excursion (apply run rest))) - -(defun embark--universal-argument (&rest _) - "Run action with a universal prefix argument." - (setq prefix-arg '(4))) - -;;; keymaps - -(defvar-keymap embark-meta-map - :doc "Keymap for non-action Embark functions." - "-" #'negative-argument - "0" #'digit-argument - "1" #'digit-argument - "2" #'digit-argument - "3" #'digit-argument - "4" #'digit-argument - "5" #'digit-argument - "6" #'digit-argument - "7" #'digit-argument - "8" #'digit-argument - "9" #'digit-argument) - -(defvar-keymap embark-general-map - :doc "Keymap for Embark general actions." - :parent embark-meta-map - "i" #'embark-insert - "w" #'embark-copy-as-kill - "q" #'embark-toggle-quit - "E" #'embark-export - "S" #'embark-collect - "L" #'embark-live - "B" #'embark-become - "A" #'embark-act-all - "C-s" #'embark-isearch-forward - "C-r" #'embark-isearch-backward - "C-SPC" #'mark - "DEL" #'delete-region - "SPC" #'embark-select) - -(defvar-keymap embark-encode-map - :doc "Keymap for Embark region encoding actions." - "r" #'rot13-region - "." #'morse-region - "-" #'unmorse-region - "s" #'studlify-region - "m" #'embark-hash-md5 - "1" #'embark-hash-sha1 - "2" #'embark-hash-sha256 - "3" #'embark-hash-sha384 - "4" #'embark-hash-sha224 - "5" #'embark-hash-sha512 - "f" #'format-encode-region - "F" #'format-decode-region - "b" #'base64-encode-region - "B" #'base64-decode-region - "u" #'embark-encode-url - "U" #'embark-decode-url - "c" #'epa-encrypt-region - "C" #'embark-epa-decrypt-region) - -(fset 'embark-encode-map embark-encode-map) - -(defvar-keymap embark-sort-map - :doc "Keymap for Embark actions that sort the region" - "l" #'sort-lines - "P" #'sort-pages - "f" #'sort-fields - "c" #'sort-columns - "p" #'sort-paragraphs - "r" #'sort-regexp-fields - "n" #'sort-numeric-fields) - -(fset 'embark-sort-map embark-sort-map) - -;; these will have autoloads in Emacs 28 -(autoload 'calc-grab-sum-down "calc" nil t) -(autoload 'calc-grab-sum-across "calc" nil t) - -;; this has had an autoload cookie since at least Emacs 26 -;; but that autoload doesn't seem to work for me -(autoload 'org-table-convert-region "org-table" nil t) - -(defvar-keymap embark-region-map - :doc "Keymap for Embark actions on the active region." - :parent embark-general-map - "u" #'upcase-region - "l" #'downcase-region - "c" #'capitalize-region - "|" #'shell-command-on-region - "e" #'eval-region - "<" #'embark-eval-replace - "a" #'align - "A" #'align-regexp - "<left>" #'indent-rigidly - "<right>" #'indent-rigidly - "TAB" #'indent-region - "f" #'fill-region - "p" #'fill-region-as-paragraph - "$" #'ispell-region - "=" #'count-words-region - "F" #'whitespace-cleanup-region - "t" #'transpose-regions - "o" #'org-table-convert-region - ";" #'comment-or-uncomment-region - "W" #'write-region - "+" #'append-to-file - "m" #'apply-macro-to-region-lines - "n" #'narrow-to-region - "*" #'calc-grab-region - ":" #'calc-grab-sum-down - "_" #'calc-grab-sum-across - "r" #'reverse-region - "d" #'delete-duplicate-lines - "b" #'browse-url-of-region - "h" #'shr-render-region - "'" #'expand-region-abbrevs - "v" #'vc-region-history - "R" #'repunctuate-sentences - "s" 'embark-sort-map - ">" 'embark-encode-map) - -(defvar-keymap embark-vc-file-map - :doc "Keymap for Embark VC file actions." - "d" #'vc-delete-file - "r" #'vc-rename-file - "i" #'vc-ignore) - -(fset 'embark-vc-file-map embark-vc-file-map) - -(defvar-keymap embark-file-map - :doc "Keymap for Embark file actions." - :parent embark-general-map - "RET" #'find-file - "f" #'find-file - "F" #'find-file-literally - "o" #'find-file-other-window - "d" #'delete-file - "D" #'delete-directory - "r" #'rename-file - "c" #'copy-file - "s" #'make-symbolic-link - "j" #'embark-dired-jump - "!" #'shell-command - "&" #'async-shell-command - "$" #'eshell - "<" #'insert-file - "m" #'chmod - "=" #'ediff-files - "+" #'make-directory - "\\" #'embark-recentf-remove - "I" #'embark-insert-relative-path - "W" #'embark-save-relative-path - "x" #'embark-open-externally - "e" #'eww-open-file - "l" #'load-file - "b" #'byte-compile-file - "R" #'byte-recompile-directory - "v" 'embark-vc-file-map) - -(defvar-keymap embark-kill-ring-map - :doc "Keymap for `kill-ring' commands." - :parent embark-general-map - "\\" #'embark-kill-ring-remove) - -(defvar-keymap embark-url-map - :doc "Keymap for Embark url actions." - :parent embark-general-map - "RET" #'browse-url - "b" #'browse-url - "d" #'embark-download-url - "x" #'embark-open-externally - "e" #'eww) - -(defvar-keymap embark-email-map - :doc "Keymap for Embark email actions." - :parent embark-general-map - "RET" #'embark-compose-mail - "c" #'embark-compose-mail) - -(defvar-keymap embark-library-map - :doc "Keymap for operations on Emacs Lisp libraries." - :parent embark-general-map - "RET" #'find-library - "l" #'load-library - "f" #'find-library - "h" #'finder-commentary - "a" #'apropos-library - "L" #'locate-library - "m" #'info-display-manual - "$" #'eshell) - -(defvar-keymap embark-buffer-map - :doc "Keymap for Embark buffer actions." - :parent embark-general-map - "RET" #'switch-to-buffer - "k" #'kill-buffer - "b" #'switch-to-buffer - "o" #'switch-to-buffer-other-window - "z" #'embark-bury-buffer - "K" #'embark-kill-buffer-and-window - "r" #'embark-rename-buffer - "=" #'ediff-buffers - "|" #'embark-shell-command-on-buffer - "<" #'insert-buffer - "$" #'eshell) - -(defvar-keymap embark-tab-map - :doc "Keymap for actions for tab-bar tabs." - :parent embark-general-map - "RET" #'tab-bar-select-tab-by-name - "s" #'tab-bar-select-tab-by-name - "r" #'tab-bar-rename-tab-by-name - "k" #'tab-bar-close-tab-by-name) - -(defvar-keymap embark-identifier-map - :doc "Keymap for Embark identifier actions." - :parent embark-general-map - "RET" #'xref-find-definitions - "h" #'display-local-help - "H" #'embark-toggle-highlight - "d" #'xref-find-definitions - "r" #'xref-find-references - "a" #'xref-find-apropos - "s" #'info-lookup-symbol - "n" #'embark-next-symbol - "p" #'embark-previous-symbol - "'" #'expand-abbrev - "$" #'ispell-word - "o" #'occur) - -(defvar-keymap embark-expression-map - :doc "Keymap for Embark expression actions." - :parent embark-general-map - "RET" #'pp-eval-expression - "e" #'pp-eval-expression - "<" #'embark-eval-replace - "m" #'pp-macroexpand-expression - "TAB" #'indent-region - "r" #'raise-sexp - ";" #'comment-dwim - "t" #'transpose-sexps - "k" #'kill-region - "u" #'backward-up-list - "n" #'forward-list - "p" #'backward-list) - -(defvar-keymap embark-defun-map - :doc "Keymap for Embark defun actions." - :parent embark-expression-map - "RET" #'embark-pp-eval-defun - "e" #'embark-pp-eval-defun - "c" #'compile-defun - "D" #'edebug-defun - "o" #'checkdoc-defun - "N" #'narrow-to-defun) - -;; Use quoted symbols to avoid byte-compiler warnings. -(defvar-keymap embark-heading-map - :doc "Keymap for Embark heading actions." - :parent embark-general-map - "RET" 'outline-show-subtree - "TAB" 'outline-cycle ;; New in Emacs 28! - "C-SPC" 'outline-mark-subtree - "n" 'outline-next-visible-heading - "p" 'outline-previous-visible-heading - "f" 'outline-forward-same-level - "b" 'outline-backward-same-level - "^" 'outline-move-subtree-up - "v" 'outline-move-subtree-down - "u" 'outline-up-heading - "+" 'outline-show-subtree - "-" 'outline-hide-subtree - ">" 'outline-demote - "<" 'outline-promote) - -(defvar-keymap embark-symbol-map - :doc "Keymap for Embark symbol actions." - :parent embark-identifier-map - "RET" #'embark-find-definition - "h" #'describe-symbol - "s" #'embark-info-lookup-symbol - "d" #'embark-find-definition - "e" #'pp-eval-expression - "a" #'apropos - "\\" #'embark-history-remove) - -(defvar-keymap embark-face-map - :doc "Keymap for Embark face actions." - :parent embark-symbol-map - "h" #'describe-face - "c" #'customize-face - "+" #'make-face-bold - "-" #'make-face-unbold - "/" #'make-face-italic - "|" #'make-face-unitalic - "!" #'invert-face - "f" #'set-face-foreground - "b" #'set-face-background) - -(defvar-keymap embark-variable-map - :doc "Keymap for Embark variable actions." - :parent embark-symbol-map - "=" #'set-variable - "c" #'customize-set-variable - "u" #'customize-variable - "v" #'embark-save-variable-value - "<" #'embark-insert-variable-value - "t" #'embark-toggle-variable) - -(defvar-keymap embark-function-map - :doc "Keymap for Embark function actions." - :parent embark-symbol-map - "m" #'elp-instrument-function ;; m=measure - "M" 'elp-restore-function ;; quoted, not autoloaded - "k" #'debug-on-entry ;; breaKpoint (running out of letters, really) - "K" #'cancel-debug-on-entry - "t" #'trace-function - "T" 'untrace-function) ;; quoted, not autoloaded - -(defvar-keymap embark-command-map - :doc "Keymap for Embark command actions." - :parent embark-function-map - "x" #'execute-extended-command - "I" #'Info-goto-emacs-command-node - "b" #'where-is - "g" #'global-set-key - "l" #'local-set-key) - -(defvar-keymap embark-package-map - :doc "Keymap for Embark package actions." - :parent embark-general-map - "RET" #'describe-package - "h" #'describe-package - "i" #'package-install - "I" #'embark-insert - "d" #'package-delete - "r" #'package-reinstall - "u" #'embark-browse-package-url - "W" #'embark-save-package-url - "a" #'package-autoremove - "g" #'package-refresh-contents - "m" #'elp-instrument-package ;; m=measure - "M" (if (fboundp 'embark-elp-restore-package) - 'embark-elp-restore-package - 'elp-restore-package)) - -(defvar-keymap embark-bookmark-map - :doc "Keymap for Embark bookmark actions." - :parent embark-general-map - "RET" #'bookmark-jump - "s" #'bookmark-set - "d" #'bookmark-delete - "r" #'bookmark-rename - "R" #'bookmark-relocate - "l" #'bookmark-locate - "<" #'bookmark-insert - "j" #'bookmark-jump - "o" #'bookmark-jump-other-window - "f" #'bookmark-jump-other-frame - "a" 'bookmark-show-annotation - "e" 'bookmark-edit-annotation - "x" #'embark-bookmark-open-externally - "$" #'eshell) - -(defvar-keymap embark-unicode-name-map - :doc "Keymap for Embark Unicode name actions." - :parent embark-general-map - "RET" #'insert-char - "I" #'insert-char - "W" #'embark-save-unicode-character) - -(defvar-keymap embark-prose-map - :doc "Keymap for Embark actions for dealing with prose." - :parent embark-general-map - "$" #'ispell-region - "f" #'fill-region - "u" #'upcase-region - "l" #'downcase-region - "c" #'capitalize-region - "F" #'whitespace-cleanup-region - "=" #'count-words-region) - -(defvar-keymap embark-sentence-map - :doc "Keymap for Embark actions for dealing with sentences." - :parent embark-prose-map - "t" #'transpose-sentences - "n" #'forward-sentence - "p" #'backward-sentence) - -(defvar-keymap embark-paragraph-map - :doc "Keymap for Embark actions for dealing with paragraphs." - :parent embark-prose-map - "t" #'transpose-paragraphs - "n" #'forward-paragraph - "p" #'backward-paragraph - "R" #'repunctuate-sentences) - -(defvar-keymap embark-flymake-map - :doc "Keymap for Embark actions on Flymake diagnostics." - :parent embark-general-map - "RET" 'flymake-show-buffer-diagnostics - "n" 'flymake-goto-next-error - "p" 'flymake-goto-prev-error) - -(defvar-keymap embark-become-help-map - :doc "Keymap for Embark help actions." - :parent embark-meta-map - "V" #'apropos-variable - "U" #'apropos-user-option - "C" #'apropos-command - "v" #'describe-variable - "f" #'describe-function - "s" #'describe-symbol - "F" #'describe-face - "p" #'describe-package - "i" #'describe-input-method) - -(autoload 'recentf-open-files "recentf" nil t) - -(defvar-keymap embark-become-file+buffer-map - :doc "Embark become keymap for files and buffers." - :parent embark-meta-map - "f" #'find-file - "4 f" #'find-file-other-window - "." #'find-file-at-point - "p" #'project-find-file - "r" #'recentf-open-files - "b" #'switch-to-buffer - "4 b" #'switch-to-buffer-other-window - "l" #'locate - "L" #'find-library - "v" #'vc-dir) - -(defvar-keymap embark-become-shell-command-map - :doc "Embark become keymap for shell commands." - :parent embark-meta-map - "!" #'shell-command - "&" #'async-shell-command - "c" #'comint-run - "t" #'term) - -(defvar-keymap embark-become-match-map - :doc "Embark become keymap for search." - :parent embark-meta-map - "o" #'occur - "k" #'keep-lines - "f" #'flush-lines - "c" #'count-matches) - -(provide 'embark) - -;; Check that embark-consult is installed. If Embark is used in -;; combination with Consult, you should install the integration package, -;; such that features like embark-export from consult-grep work as -;; expected. - -(with-eval-after-load 'consult - (unless (require 'embark-consult nil 'noerror) - (warn "The package embark-consult should be installed if you use both Embark and Consult"))) - -(with-eval-after-load 'org - (require 'embark-org)) - -;;; embark.el ends here diff --git a/emacs/elpa/embark-20240419.452/embark.elc b/emacs/elpa/embark-20240419.452/embark.elc Binary files differ. diff --git a/emacs/elpa/embark-20240419.452/dir b/emacs/elpa/embark-20240607.2213/dir diff --git a/emacs/elpa/embark-20240419.452/embark-autoloads.el b/emacs/elpa/embark-20240607.2213/embark-autoloads.el diff --git a/emacs/elpa/embark-20240419.452/embark-org.el b/emacs/elpa/embark-20240607.2213/embark-org.el diff --git a/emacs/elpa/embark-20240419.452/embark-org.elc b/emacs/elpa/embark-20240607.2213/embark-org.elc Binary files differ. diff --git a/emacs/elpa/embark-20240607.2213/embark-pkg.el b/emacs/elpa/embark-20240607.2213/embark-pkg.el @@ -0,0 +1,15 @@ +(define-package "embark" "20240607.2213" "Conveniently act on minibuffer completions" + '((emacs "27.1") + (compat "29.1.4.0")) + :commit "9c166c4b96a0b1e85401bcc6fb95ce021e7b5013" :authors + '(("Omar Antolín Camarena" . "omar@matem.unam.mx")) + :maintainers + '(("Omar Antolín Camarena" . "omar@matem.unam.mx")) + :maintainer + '("Omar Antolín Camarena" . "omar@matem.unam.mx") + :keywords + '("convenience") + :url "https://github.com/oantolin/embark") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs/elpa/embark-20240607.2213/embark.el b/emacs/elpa/embark-20240607.2213/embark.el @@ -0,0 +1,4604 @@ +;;; embark.el --- Conveniently act on minibuffer completions -*- lexical-binding: t; -*- + +;; Copyright (C) 2021-2023 Free Software Foundation, Inc. + +;; Author: Omar Antolín Camarena <omar@matem.unam.mx> +;; Maintainer: Omar Antolín Camarena <omar@matem.unam.mx> +;; Keywords: convenience +;; Version: 1.1 +;; Homepage: https://github.com/oantolin/embark +;; Package-Requires: ((emacs "27.1") (compat "29.1.4.0")) + +;; This file is part of GNU Emacs. + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This package provides a sort of right-click contextual menu for +;; Emacs, accessed through the `embark-act' command (which you should +;; bind to a convenient key), offering you relevant actions to use on +;; a target determined by the context: + +;; - In the minibuffer, the target is the current best completion +;; candidate. +;; - In the `*Completions*' buffer the target is the completion at point. +;; - In a regular buffer, the target is the region if active, or else the +;; file, symbol or url at point. + +;; The type of actions offered depend on the type of the target: + +;; - For files you get offered actions like deleting, copying, +;; renaming, visiting in another window, running a shell command on the +;; file, etc. +;; - For buffers the actions include switching to or killing the buffer. +;; - For package names the actions include installing, removing or +;; visiting the homepage. + +;; Everything is easily configurable: determining the current target, +;; classifying it, and deciding with actions are offered for each type +;; in the classification. The above introduction just mentions part of +;; the default configuration. + +;; Configuring which actions are offered for a type is particularly +;; easy and requires no programming: the `embark-keymap-alist' +;; variable associates target types with variable containing keymaps, +;; and those keymaps containing binds for the actions. For example, +;; in the default configuration the type `file' is associated with the +;; symbol `embark-file-map'. That symbol names a keymap with +;; single-letter key bindings for common Emacs file commands, for +;; instance `c' is bound to `copy-file'. This means that if while you +;; are in the minibuffer after running a command that prompts for a +;; file, such as `find-file' or `rename-file', you can copy a file by +;; running `embark-act' and then pressing `c'. + +;; These action keymaps are very convenient but not strictly necessary +;; when using `embark-act': you can use any command that reads from the +;; minibuffer as an action and the target of the action will be inserted +;; at the first minibuffer prompt. After running `embark-act' all of your +;; key bindings and even `execute-extended-command' can be used to run a +;; command. The action keymaps are normal Emacs keymaps and you should +;; feel free to bind in them whatever commands you find useful as actions. + +;; The actions in `embark-general-map' are available no matter what +;; type of completion you are in the middle of. By default this +;; includes bindings to save the current candidate in the kill ring +;; and to insert the current candidate in the previously selected +;; buffer (the buffer that was current when you executed a command +;; that opened up the minibuffer). + +;; You can read about the Embark GitHub project wiki: +;; https://github.com/oantolin/embark/wiki/Default-Actions + +;; Besides acting individually on targets, Embark lets you work +;; collectively on a set of target candidates. For example, while +;; you are in the minibuffer the candidates are simply the possible +;; completions of your input. Embark provides three commands to work +;; on candidate sets: + +;; - The `embark-act-all' command runs the same action on each of the +;; current candidates. It is just like using `embark-act' on each +;; candidate in turn. + +;; - The `embark-collect' command produces a buffer listing all +;; candidates, for you to peruse and run actions on at your leisure. +;; The candidates are displayed as a list showing additional +;; annotations. + +;; - The `embark-export' command tries to open a buffer in an +;; appropriate major mode for the set of candidates. If the +;; candidates are files export produces a Dired buffer; if they are +;; buffers, you get an Ibuffer buffer; and if they are packages you +;; get a buffer in package menu mode. + +;; These are always available as "actions" (although they do not act +;; on just the current target but on all candidates) for embark-act +;; and are bound to A, S (for "snapshot") and E, respectively, in +;; embark-general-map. This means that you do not have to bind your +;; own key bindings for these (although you can, of course), just a +;; key binding for `embark-act'. + +;;; Code: + +(require 'compat) +(eval-when-compile (require 'subr-x)) + +(require 'ffap) ; used to recognize file and url targets + +;;; User facing options + +(defgroup embark nil + "Emacs Mini-Buffer Actions Rooted in Keymaps." + :link '(info-link :tag "Info Manual" "(embark)") + :link '(url-link :tag "Homepage" "https://github.com/oantolin/embark") + :link '(emacs-library-link :tag "Library Source" "embark.el") + :group 'minibuffer + :prefix "embark-") + +(defcustom embark-keymap-alist + '((file embark-file-map) + (library embark-library-map) + (environment-variables embark-file-map) ; they come up in file completion + (url embark-url-map) + (email embark-email-map) + (buffer embark-buffer-map) + (tab embark-tab-map) + (expression embark-expression-map) + (identifier embark-identifier-map) + (defun embark-defun-map) + (symbol embark-symbol-map) + (face embark-face-map) + (command embark-command-map) + (variable embark-variable-map) + (function embark-function-map) + (minor-mode embark-command-map) + (unicode-name embark-unicode-name-map) + (package embark-package-map) + (bookmark embark-bookmark-map) + (region embark-region-map) + (sentence embark-sentence-map) + (paragraph embark-paragraph-map) + (kill-ring embark-kill-ring-map) + (heading embark-heading-map) + (flymake embark-flymake-map) + (smerge smerge-basic-map embark-general-map) + (t embark-general-map)) + "Alist of action types and corresponding keymaps. +The special key t is associated with the default keymap to use. +Each value can be either a single symbol whose value is a keymap, +or a list of such symbols." + :type '(alist :key-type (symbol :tag "Target type") + :value-type (choice (variable :tag "Keymap") + (repeat :tag "Keymaps" variable)))) + +(defcustom embark-target-finders + '(embark-target-top-minibuffer-candidate + embark-target-active-region + embark-target-collect-candidate + embark-target-completion-list-candidate + embark-target-text-heading-at-point + embark-target-bug-reference-at-point + embark-target-flymake-at-point + embark-target-smerge-at-point + embark-target-package-at-point + embark-target-email-at-point + embark-target-url-at-point + embark-target-file-at-point + embark-target-custom-variable-at-point + embark-target-identifier-at-point + embark-target-guess-file-at-point + embark-target-expression-at-point + embark-target-sentence-at-point + embark-target-paragraph-at-point + embark-target-defun-at-point + embark-target-prog-heading-at-point) + "List of functions to determine the target in current context. +Each function should take no arguments and return one of: + +1. a cons (TYPE . TARGET) where TARGET is a string and TYPE is a + symbol (which is looked up in `embark-keymap-alist' to + determine which additional keybindings for actions to setup); + +2. a dotted list of the form (TYPE TARGET START . END), where + START and END are the buffer positions bounding TARGET, used + for highlighting; or + +3. a possibly empty list of targets, each of type 1 or 2 (in + particular if a target finder does not find any targets, it + should return nil)." + :type 'hook) + +(defcustom embark-transformer-alist + '((minor-mode . embark--lookup-lighter-minor-mode) + (embark-keybinding . embark--keybinding-command) + (project-file . embark--project-file-full-path) + (package . embark--remove-package-version) + (multi-category . embark--refine-multi-category) + (file . embark--simplify-path)) + "Alist associating type to functions for transforming targets. +Each function should take a type and a target string and return a +pair of the form a `cons' of the new type and the new target." + :type '(alist :key-type symbol :value-type function)) + +(defcustom embark-become-keymaps + '(embark-become-help-map + embark-become-file+buffer-map + embark-become-shell-command-map + embark-become-match-map) + "List of keymaps for `embark-become'. +Each keymap groups a set of related commands that can +conveniently become one another." + :type '(repeat variable)) + +(defcustom embark-prompter 'embark-keymap-prompter + "Function used to prompt the user for actions. +This should be set to a function that prompts the use for an +action and returns the symbol naming the action command. The +default value, `embark-keymap-prompter' activates the type +specific action keymap given in `embark-keymap-alist'. +There is also `embark-completing-read-prompter' which +prompts for an action with completion." + :type '(choice (const :tag "Use action keymaps" embark-keymap-prompter) + (const :tag "Read action with completion" + embark-completing-read-prompter) + (function :tag "Other"))) + +(defcustom embark-keymap-prompter-key "@" + "Key to switch to the keymap prompter from `embark-completing-read-prompter'. + +The key must be either nil or a string. The +string must be accepted by `key-valid-p'." + :type '(choice key (const :tag "None" nil))) + +(defcustom embark-cycle-key nil + "Key used for `embark-cycle'. + +If the key is set to nil it defaults to the global binding of +`embark-act'. The key must be a string which is accepted by +`key-valid-p'." + :type '(choice key (const :tag "Use embark-act key" nil))) + +(defcustom embark-help-key "C-h" + "Key used for help. + +The key must be either nil or a string. The +string must be accepted by `key-valid-p'." + :type '(choice (const "C-h") + (const "?") + (const :tag "None" nil) + key)) + +(defcustom embark-keybinding-repeat + (propertize "*" 'face 'embark-keybinding-repeat) + "Indicator string for repeatable keybindings. +Keybindings are formatted by the `completing-read' prompter and +the verbose indicator." + :type 'string) + +(defface embark-keybinding-repeat + '((t :inherit font-lock-builtin-face)) + "Face used to indicate keybindings as repeatable.") + +(defface embark-keybinding '((t :inherit success)) + "Face used to display key bindings. +Used by `embark-completing-read-prompter' and `embark-keymap-help'.") + +(defface embark-keymap '((t :slant italic)) + "Face used to display keymaps. +Used by `embark-completing-read-prompter' and `embark-keymap-help'.") + +(defface embark-target '((t :inherit highlight)) + "Face used to highlight the target at point during `embark-act'.") + +(defcustom embark-quit-after-action t + "Should `embark-act' quit the minibuffer? +This controls whether calling `embark-act' without a prefix +argument quits the minibuffer or not. You can always get the +opposite behavior to that indicated by this variable by calling +`embark-act' with \\[universal-argument]. + +Note that `embark-act' can also be called from outside the +minibuffer and this variable is irrelevant in that case. + +In addition to t or nil this variable can also be set to an +alist to specify the minibuffer quitting behavior per command. +In the alist case one can additionally use the key t to +prescribe a default for commands not used as alist keys." + :type '(choice (const :tag "Always quit" t) + (const :tag "Never quit" nil) + (alist :tag "Configure per action" + :key-type (choice (function :tag "Action") + (const :tag "All other actions" t)) + :value-type (choice (const :tag "Quit" t) + (const :tag "Do not quit" nil))))) + +(defcustom embark-confirm-act-all t + "Should `embark-act-all' prompt the user for confirmation? +Even if this variable is nil you may still be prompted to confirm +some uses of `embark-act-all', namely, for those actions whose +entry in `embark-pre-action-hooks' includes `embark--confirm'." + :type 'boolean) + +(defcustom embark-default-action-overrides nil + "Alist associating target types with overriding default actions. +When the source of a target is minibuffer completion, the default +action for it is usually the command that opened the minibuffer +in the first place but this can be overridden for a given type by +an entry in this list. + +For example, if you run `delete-file' the default action for its +completion candidates is `delete-file' itself. You may prefer to +make `find-file' the default action for all files, even if they +were obtained from a `delete-file' prompt. In that case you can +configure that by adding an entry to this variable pairing `file' +with `find-file'. + +In addition to target types, you can also use as keys in this alist, +pairs of a target type and a command name. Such a pair indicates that +the override only applies if the target was obtained from minibuffer +completion from that command. For example adding an +entry (cons (cons \\='file \\='delete-file) \\='find-file) to this alist would +indicate that for files at the prompt of the `delete-file' command, +`find-file' should be used as the default action." + :type '(alist :key-type (choice (symbol :tag "Type") + (cons (symbol :tag "Type") + (symbol :tag "Command"))) + :value-type (function :tag "Default action"))) + +(defcustom embark-target-injection-hooks + '((async-shell-command embark--allow-edit embark--shell-prep) + (shell-command embark--allow-edit embark--shell-prep) + (pp-eval-expression embark--eval-prep) + (eval-expression embark--eval-prep) + (package-delete embark--force-complete) + ;; commands evaluating code found in the buffer, which may in turn prompt + (embark-pp-eval-defun embark--ignore-target) + (eval-defun embark--ignore-target) + (eval-last-sexp embark--ignore-target) + (embark-eval-replace embark--ignore-target) + ;; commands which prompt for something that is *not* the target + (write-region embark--ignore-target) + (append-to-file embark--ignore-target) + (append-to-buffer embark--ignore-target) + (shell-command-on-region embark--ignore-target) + (format-encode-region embark--ignore-target) + (format-decode-region embark--ignore-target) + (xref-find-definitions embark--ignore-target) + (xref-find-references embark--ignore-target) + (sort-regexp-fields embark--ignore-target) + (align-regexp embark--ignore-target)) + "Alist associating commands with post-injection setup hooks. +For commands appearing as keys in this alist, run the +corresponding value as a setup hook after injecting the target +into in the minibuffer and before acting on it. The hooks must +accept arbitrary keyword arguments. The :action command, the +:target string and target :type are always present. For actions +at point the target :bounds are passed too. The default pre-action +hook is specified by the entry with key t. Furthermore, hooks with +the key :always are executed always." + :type '(alist :key-type + (choice symbol + (const :tag "Default" t) + (const :tag "Always" :always)) + :value-type hook)) + +(defcustom embark-pre-action-hooks + `(;; commands that need to position point at the beginning or end + (eval-last-sexp embark--end-of-target) + (indent-pp-sexp embark--beginning-of-target) + (backward-up-list embark--beginning-of-target) + (backward-list embark--beginning-of-target) + (forward-list embark--end-of-target) + (forward-sexp embark--end-of-target) + (backward-sexp embark--beginning-of-target) + (raise-sexp embark--beginning-of-target) + (kill-sexp embark--beginning-of-target) + (mark-sexp embark--beginning-of-target) + (transpose-sexps embark--end-of-target) + (transpose-sentences embark--end-of-target) + (transpose-paragraphs embark--end-of-target) + (forward-sentence embark--end-of-target) + (backward-sentence embark--beginning-of-target) + (backward-paragraph embark--beginning-of-target) + (embark-insert embark--end-of-target) + ;; commands we want to be able to jump back from + ;; (embark-find-definition achieves this by calling + ;; xref-find-definitions which pushes the markers itself) + (find-library embark--xref-push-marker) + ;; commands which prompt the user for confirmation before running + (delete-file embark--confirm) + (delete-directory embark--confirm) + (kill-buffer embark--confirm) + (embark-kill-buffer-and-window embark--confirm) + (bookmark-delete embark--confirm) + (package-delete embark--confirm) + (,'tab-bar-close-tab-by-name embark--confirm) ;; Avoid package-lint warning + ;; search for region contents outside said region + (embark-isearch-forward embark--unmark-target) + (embark-isearch-backward embark--unmark-target) + (occur embark--unmark-target) + (query-replace embark--beginning-of-target embark--unmark-target) + (query-replace-regexp embark--beginning-of-target embark--unmark-target) + (replace-string embark--beginning-of-target embark--unmark-target) + (replace-regexp embark--beginning-of-target embark--unmark-target) + ;; mark pseudo-action + (mark embark--mark-target) + ;; shells in new buffers + (shell embark--universal-argument) + (eshell embark--universal-argument)) + "Alist associating commands with pre-action hooks. +The hooks are run right before an action is embarked upon. See +`embark-target-injection-hooks' for information about the hook +arguments and more details." + :type '(alist :key-type + (choice symbol + (const :tag "Default" t) + (const :tag "Always" :always)) + :value-type hook)) + +(defcustom embark-post-action-hooks + `((bookmark-delete embark--restart) + (bookmark-rename embark--restart) + (delete-file embark--restart) + (embark-kill-ring-remove embark--restart) + (embark-recentf-remove embark--restart) + (embark-history-remove embark--restart) + (rename-file embark--restart) + (copy-file embark--restart) + (delete-directory embark--restart) + (make-directory embark--restart) + (kill-buffer embark--restart) + (embark-rename-buffer embark--restart) + (,'tab-bar-rename-tab-by-name embark--restart) ;; Avoid package-lint warning + (,'tab-bar-close-tab-by-name embark--restart) + (package-delete embark--restart)) + "Alist associating commands with post-action hooks. +The hooks are run after an embarked upon action concludes. See +`embark-target-injection-hooks' for information about the hook +arguments and more details." + :type '(alist :key-type + (choice symbol + (const :tag "Default" t) + (const :tag "Always" :always)) + :value-type hook)) + +(defcustom embark-around-action-hooks + '(;; use directory of target as default-directory + (shell embark--cd) + (eshell embark--cd) + ;; mark the target preserving point and previous mark + (kill-region embark--mark-target) + (kill-ring-save embark--mark-target) + (indent-region embark--mark-target) + (ispell-region embark--mark-target) + (fill-region embark--mark-target) + (upcase-region embark--mark-target) + (downcase-region embark--mark-target) + (capitalize-region embark--mark-target) + (count-words-region embark--mark-target) + (count-words embark--mark-target) + (delete-duplicate-lines embark--mark-target) + (shell-command-on-region embark--mark-target) + (delete-region embark--mark-target) + (format-encode-region embark--mark-target) + (format-decode-region embark--mark-target) + (write-region embark--mark-target) + (append-to-file embark--mark-target) + (append-to-buffer embark--mark-target) + (shell-command-on-region embark--mark-target) + (embark-eval-replace embark--mark-target) + (delete-indentation embark--mark-target) + (comment-dwim embark--mark-target) + (insert-parentheses embark--mark-target) + (insert-pair embark--mark-target) + (org-emphasize embark--mark-target) + ;; do the actual work of selecting & deselecting targets + (embark-select embark--select)) + "Alist associating commands with post-action hooks. +The hooks are run instead of the embarked upon action. The hook +can decide whether or not to run the action or it can run it +in some special environment, like inside a let-binding or inside +`save-excursion'. Each hook is called with keyword argument :run +providing a function encapsulating the following around hooks and +the action; the hook additionally receives the keyword arguments +used for other types of action hooks, for more details see +`embark-target-injection-hooks'." + :type '(alist :key-type + (choice symbol + (const :tag "Default" t) + (const :tag "Always" :always)) + :value-type hook)) + +(when (version-list-< (version-to-list emacs-version) '(29 1)) + ;; narrow to target for duration of action + (setf (alist-get 'repunctuate-sentences embark-around-action-hooks) + '(embark--narrow-to-target))) + +(defcustom embark-multitarget-actions '(embark-insert embark-copy-as-kill) + "Commands for which `embark-act-all' should pass a list of targets. +Normally `embark-act-all' runs the same action on each candidate +separately, but when a command included in this variable's value +is used as an action, `embark-act-all' will instead call it +non-interactively with a single argument: the list of all +candidates. For commands on this list `embark-act' behaves +similarly: it calls them non-interactively with a single +argument: a one element list containing the target." + :type '(repeat function)) + +(defcustom embark-repeat-actions + '((mark . region) + ;; outline commands + outline-next-visible-heading outline-previous-visible-heading + outline-forward-same-level outline-backward-same-level + outline-demote outline-promote + outline-show-subtree (outline-mark-subtree . region) + outline-move-subtree-up outline-move-subtree-down + outline-up-heading outline-hide-subtree outline-cycle + ;; org commands (remapped outline commands) + org-forward-heading-same-level org-backward-heading-same-level + org-next-visible-heading org-previous-visible-heading + org-demote-subtree org-promote-subtree + org-show-subtree (org-mark-subtree . region) + org-move-subtree-up org-move-subtree-down + ;; transpose commands + transpose-sexps transpose-sentences transpose-paragraphs + ;; navigation commands + flymake-goto-next-error flymake-goto-prev-error + embark-next-symbol embark-previous-symbol + backward-up-list backward-list forward-list forward-sexp + backward-sexp forward-sentence backward-sentence + forward-paragraph backward-paragraph + ;; smerge commands + smerge-refine smerge-combine-with-next smerge-prev smerge-next) + "List of repeatable actions. +When you use a command on this list as an Embark action from +outside the minibuffer, `embark-act' does not exit but instead +lets you act again on the possibly new target you reach. + +By default, after using one of these actions, when `embark-act' +looks for targets again, it will start the target cycle at the +same type as the previously acted upon target; that is, you +\"don't loose your place in the target cycle\". + +Sometimes, however, you'll want to prioritize a different type of +target to continue acting on. The main example of this that if +you use a marking command as an action, you almost always want to +act on the region next. For those cases, in addition to +commands, you can also place on this list a pair of a command and +the desired starting type for the target cycle for the next +action." + :type '(repeat (choice function + (cons function + (symbol :tag "Next target type"))))) + +;;; Overlay properties + +;; high priority to override both bug reference and the lazy +;; isearch highlights in embark-isearch-highlight-indicator +(put 'embark-target-overlay 'face 'embark-target) +(put 'embark-target-overlay 'priority 1001) +(put 'embark-selected-overlay 'face 'embark-selected) +(put 'embark-selected-overlay 'priority 1001) + +;;; Stashing information for actions in buffer local variables + +(defvar-local embark--type nil + "Cache for the completion type, meant to be set buffer-locally.") + +(defvar-local embark--target-buffer nil + "Cache for the previous buffer, meant to be set buffer-locally.") + +(defvar-local embark--target-window nil + "Cache for the previous window, meant to be set buffer-locally. +Since windows can be reused to display different buffers, this +window should only be used if it displays the buffer stored in +the variable `embark--target-buffer'.") + +(defvar-local embark--command nil + "Command that started the completion session.") + +(defvar-local embark--toggle-quit nil + "Should we toggle the default quitting behavior for the next action?") + +(defun embark--minibuffer-point () + "Return length of minibuffer contents." + (max 0 (- (point) (minibuffer-prompt-end)))) + +(defun embark--default-directory () + "Guess a reasonable default directory for the current candidates." + (if (and (minibufferp) minibuffer-completing-file-name) + (let ((end (minibuffer-prompt-end)) + (contents (minibuffer-contents))) + (expand-file-name + (substitute-in-file-name + (buffer-substring + end + (+ end + (or (cdr + (last + (completion-all-completions + contents + minibuffer-completion-table + minibuffer-completion-predicate + (embark--minibuffer-point)))) + (cl-position ?/ contents :from-end t) + 0)))))) + default-directory)) + +(defun embark--target-buffer () + "Return buffer that should be targeted by Embark actions." + (cond + ((and (minibufferp) minibuffer-completion-table (minibuffer-selected-window)) + (window-buffer (minibuffer-selected-window))) + ((and embark--target-buffer (buffer-live-p embark--target-buffer)) + embark--target-buffer) + (t (current-buffer)))) + +(defun embark--target-window (&optional display) + "Return window which should be selected when Embark actions run. +If DISPLAY is non-nil, call `display-buffer' to produce the +window if necessary." + (cond + ((and (minibufferp) minibuffer-completion-table (minibuffer-selected-window)) + (minibuffer-selected-window)) + ((and embark--target-window + (window-live-p embark--target-window) + (or (not (buffer-live-p embark--target-buffer)) + (eq (window-buffer embark--target-window) embark--target-buffer))) + embark--target-window) + ((and embark--target-buffer (buffer-live-p embark--target-buffer)) + (or (get-buffer-window embark--target-buffer) + (when display (display-buffer embark--target-buffer)))) + (display (selected-window)))) + +(defun embark--cache-info (buffer) + "Cache information needed for actions in variables local to BUFFER. +BUFFER defaults to the current buffer." + (let ((cmd embark--command) + (dir (embark--default-directory)) + (target-buffer (embark--target-buffer)) + (target-window (embark--target-window))) + (with-current-buffer buffer + (setq embark--command cmd + default-directory dir + embark--target-buffer target-buffer + embark--target-window target-window)))) + +(defun embark--cache-info--completion-list () + "Cache information needed for actions in a *Completions* buffer. +Meant to be be added to `completion-setup-hook'." + ;; when completion-setup-hook hook runs, the *Completions* buffer is + ;; available in the variable standard-output + (embark--cache-info standard-output) + (with-current-buffer standard-output + (when (minibufferp completion-reference-buffer) + (setq embark--type + (completion-metadata-get + (with-current-buffer completion-reference-buffer + (embark--metadata)) + 'category))))) + +;; We have to add this *after* completion-setup-function because that's +;; when the buffer is put in completion-list-mode and turning the mode +;; on kills all local variables! So we use a depth of 5. +(add-hook 'completion-setup-hook #'embark--cache-info--completion-list 5) + +;;;###autoload +(progn + (defun embark--record-this-command () + "Record command which opened the minibuffer. +We record this because it will be the default action. +This function is meant to be added to `minibuffer-setup-hook'." + (setq-local embark--command this-command)) + (add-hook 'minibuffer-setup-hook #'embark--record-this-command)) + +;;; Internal variables + +(defvar embark--prompter-history nil + "History used by the `embark-completing-read-prompter'.") + +;;; Core functionality + +(defconst embark--verbose-indicator-buffer " *Embark Actions*") + +(defvar embark--minimal-indicator-overlay nil) + +(defun embark--metadata () + "Return current minibuffer completion metadata." + (completion-metadata + (buffer-substring-no-properties + (minibuffer-prompt-end) + (max (minibuffer-prompt-end) (point))) + minibuffer-completion-table + minibuffer-completion-predicate)) + +(defun embark-target-active-region () + "Target the region if active." + (when (use-region-p) + (let ((start (region-beginning)) + (end (region-end))) + `(region ,(buffer-substring start end) . (,start . ,end))))) + +(autoload 'dired-get-filename "dired") +(declare-function image-dired-original-file-name "image-dired") + +(defun embark-target-guess-file-at-point () + "Target the file guessed by `ffap' at point." + (when-let ((tap-file (thing-at-point 'filename)) + ((not (ffap-url-p tap-file))) ; no URLs, those have a target finder + (bounds (bounds-of-thing-at-point 'filename)) + (file (ffap-file-at-point))) + ;; ffap doesn't make bounds available, so we use + ;; thingatpt bounds, which might be a little off + ;; adjust bounds if thingatpt gobbled punctuation around file + (when (or (string-match (regexp-quote file) tap-file) + (string-match (regexp-quote (file-name-base file)) tap-file)) + (setq bounds (cons (+ (car bounds) (match-beginning 0)) + (- (cdr bounds) (- (length tap-file) + (match-end 0)))))) + `(file ,(abbreviate-file-name (expand-file-name file)) ,@bounds))) + +(defun embark-target-file-at-point () + "Target file at point. +This function mostly relies on `ffap-file-at-point', with the +following exceptions: + +- In `dired-mode', it uses `dired-get-filename' instead. + +- In `imaged-dired-thumbnail-mode', it uses + `image-dired-original-file-name' instead." + (let (file bounds) + (or (and (derived-mode-p 'dired-mode) + (setq file (dired-get-filename t 'no-error-if-not-filep)) + (setq bounds + (cons + (save-excursion (dired-move-to-filename) (point)) + (save-excursion (dired-move-to-end-of-filename) (point))))) + (and (derived-mode-p 'image-dired-thumbnail-mode) + (setq file (image-dired-original-file-name)) + (setq bounds (cons (point) (1+ (point))))) + (when-let ((tap-file (thing-at-point 'filename)) + ((not (equal (file-name-base tap-file) tap-file))) + (guess (embark-target-guess-file-at-point))) + (setq file (cadr guess) bounds (cddr guess)))) + (when file + `(file ,(abbreviate-file-name (expand-file-name file)) ,@bounds)))) + +(defun embark-target-package-at-point () + "Target the package on the current line in a packages buffer." + (when (derived-mode-p 'package-menu-mode) + (when-let ((pkg (get-text-property (point) 'tabulated-list-id))) + `(package ,(symbol-name (package-desc-name pkg)) + ,(line-beginning-position) . ,(line-end-position))))) + +(defun embark-target-email-at-point () + "Target the email address at point." + (when-let ((email (thing-at-point 'email))) + (when (string-prefix-p "mailto:" email) + (setq email (string-remove-prefix "mailto:" email))) + `(email ,email . ,(bounds-of-thing-at-point 'email)))) + +(defun embark-target-url-at-point () + "Target the URL at point." + (if-let ((url (or (get-text-property (point) 'shr-url) + (get-text-property (point) 'image-url)))) + `(url ,url + ,(previous-single-property-change + (min (1+ (point)) (point-max)) 'mouse-face nil (point-min)) + . ,(next-single-property-change + (point) 'mouse-face nil (point-max))) + (when-let ((url (thing-at-point 'url))) + `(url ,url . ,(thing-at-point-bounds-of-url-at-point t))))) + +(declare-function widget-at "wid-edit") + +(defun embark-target-custom-variable-at-point () + "Target the variable corresponding to the customize widget at point." + (when (derived-mode-p 'Custom-mode) + (save-excursion + (beginning-of-line) + (when-let* ((widget (widget-at (point))) + (var (and (eq (car widget) 'custom-visibility) + (plist-get (cdr widget) :parent))) + (sym (and (eq (car var) 'custom-variable) + (plist-get (cdr var) :value)))) + `(variable + ,(symbol-name sym) + ,(point) + . ,(progn + (re-search-forward ":" (line-end-position) 'noerror) + (point))))))) + +;; NOTE: There is also (thing-at-point 'list), however it does +;; not work on strings and requires the point to be inside the +;; parentheses. This version here is slightly more general. +(defun embark-target-expression-at-point () + "Target expression at point." + (cl-flet ((syntax-p (class &optional (delta 0)) + (and (<= (point-min) (+ (point) delta) (point-max)) + (eq (pcase class + ('open 4) ('close 5) ('prefix 6) ('string 7)) + (syntax-class (syntax-after (+ (point) delta))))))) + (when-let + ((start + (pcase-let ((`(_ ,open _ ,string _ _ _ _ ,start _ _) (syntax-ppss))) + (ignore-errors ; set start=nil if delimiters are unbalanced + (cond + (string start) + ((or (syntax-p 'open) (syntax-p 'prefix)) + (save-excursion (backward-prefix-chars) (point))) + ((syntax-p 'close -1) + (save-excursion + (backward-sexp) (backward-prefix-chars) (point))) + ((syntax-p 'string) (point)) + ((syntax-p 'string -1) (scan-sexps (point) -1)) + (t open))))) + (end (ignore-errors (scan-sexps start 1)))) + (unless (eq start (car (bounds-of-thing-at-point 'defun))) + `(expression ,(buffer-substring start end) ,start . ,end))))) + +(defmacro embark-define-overlay-target (name prop &optional pred type target) + "Define a target finder for NAME that targets overlays with property PROP. +The function defined is named embark-target-NAME-at-point and it +returns Embark targets based on the overlays around point. An +overlay provides a target if its property named PROP is non-nil. + +If the optional PRED argument is given, it should be an +expression and it further restricts the targets to only those +overlays for which PRED evaluates to non-nil. + +The target finder returns target type NAME or optional symbol +TYPE if given. + +The target finder returns the substring of the buffer covered by +the overlay as the target string or the result of evaluating the +optional TARGET expression if given. + +PRED and TARGET are expressions (not functions) and when evaluated the +symbols `%o' and `%p' are bound to the overlay and the overlay's +property respectively." + `(defun ,(intern (format "embark-target-%s-at-point" name)) () + ,(format "Target %s at point." name) + (when-let ((%o (seq-find + (lambda (%o) + (when-let ((%p (overlay-get %o ',prop))) + (ignore %p) + ,(or pred t))) + (overlays-in (max (point-min) (1- (point))) + (min (point-max) (1+ (point)))))) + (%p (overlay-get %o ',prop))) + (ignore %p) + (cons ',(or type name) + (cons ,(or target `(buffer-substring-no-properties + (overlay-start %o) (overlay-end %o))) + (cons (overlay-start %o) (overlay-end %o))))))) + +(embark-define-overlay-target flymake flymake-diagnostic) +(embark-define-overlay-target bug-reference bug-reference-url nil url %p) +(embark-define-overlay-target smerge smerge (eq %p 'conflict)) + +(defmacro embark-define-thingatpt-target (thing &rest modes) + "Define a target finder for THING using the thingatpt library. +The function defined is named embark-target-NAME-at-point and it +uses (thing-at-point \\='THING) to find its targets. + +If any MODES are given, the target finder only applies to buffers +in one of those major modes." + (declare (indent 1)) + `(defun ,(intern (format "embark-target-%s-at-point" thing)) () + ,(format "Target %s at point." thing) + (when ,(if modes `(derived-mode-p ,@(mapcar (lambda (m) `',m) modes)) t) + (when-let (bounds (bounds-of-thing-at-point ',thing)) + (cons ',thing (cons + (buffer-substring (car bounds) (cdr bounds)) + bounds)))))) + +(embark-define-thingatpt-target defun) +(embark-define-thingatpt-target sentence + text-mode help-mode Info-mode man-common) +(embark-define-thingatpt-target paragraph + text-mode help-mode Info-mode man-common) + +(defmacro embark-define-regexp-target + (name regexp &optional type target bounds limit) + "Define a target finder for matches of REGEXP around point. +The function defined is named embark-target-NAME-at-point and it +uses (thing-at-point-looking-at REGEXP) to find its targets. + +The target finder returns target type NAME or optional symbol +TYPE if given. + +The target finder returns the substring of the buffer matched by +REGEXP as the target string or the result of evaluating the +optional TARGET expression if given. In the expression TARGET +you can use `match-string' to recover the match of the REGEXP or +of any sub-expressions it has. + +BOUNDS is an optional expression to compute the bounds of the +target and defaults to (cons (match-beginning 0) (match-end 0)). + +The optional LIMIT is the number of characters before and after +point to limit the search to. If LIMIT is nil, search a little +more than the current line (more precisely, the smallest interval +centered at point that includes the current line)." + `(defun ,(intern (format "embark-target-%s-at-point" name)) () + ,(format "Target %s at point." name) + (save-match-data + (when (thing-at-point-looking-at + ,regexp + ,(or limit '(max (- (pos-eol) (point)) (- (point) (pos-bol))))) + (cons ',(or type name) + (cons ,(or target '(match-string 0)) + ,(or bounds + '(cons (match-beginning 0) (match-end 0))))))))) + +(defun embark--identifier-types (identifier) + "Return list of target types appropriate for IDENTIFIER." + (let ((symbol (intern-soft identifier))) + (if (not + (or (derived-mode-p 'emacs-lisp-mode 'inferior-emacs-lisp-mode) + (and (not (derived-mode-p 'prog-mode)) + symbol + (or (boundp symbol) (fboundp symbol) (symbol-plist symbol))))) + '(identifier) + (let* ((library (ffap-el-mode identifier)) + (types + (append + (and (commandp symbol) '(command)) + (and symbol (boundp symbol) (not (keywordp symbol)) '(variable)) + (and (fboundp symbol) (not (commandp symbol)) '(function)) + (and (facep symbol) '(face)) + (and library '(library)) + (and (featurep 'package) (embark--package-desc symbol) + '(package))))) + (when (and library + (looking-back "\\(?:require\\|use-package\\).*" + (line-beginning-position))) + (setq types (embark--rotate types (cl-position 'library types)))) + (or types '(symbol)))))) + +(defun embark-target-identifier-at-point () + "Target identifier at point. + +In Emacs Lisp and IELM buffers the identifier is promoted to a +symbol, for which more actions are available. Identifiers are +also promoted to symbols if they are interned Emacs Lisp symbols +and found in a buffer in a major mode that is not derived from +`prog-mode' (this is intended for when you might be reading or +writing about Emacs). + +As a convenience, in Org Mode an initial ' or surrounding == or +~~ are removed." + (when-let (bounds (bounds-of-thing-at-point 'symbol)) + (let ((name (buffer-substring (car bounds) (cdr bounds)))) + (when (derived-mode-p 'org-mode) + (cond ((string-prefix-p "'" name) + (setq name (substring name 1)) + (cl-incf (car bounds))) + ((string-match-p "^\\([=~]\\).*\\1$" name) + (setq name (substring name 1 -1)) + (cl-incf (car bounds)) + (cl-decf (cdr bounds))))) + (mapcar (lambda (type) `(,type ,name . ,bounds)) + (embark--identifier-types name))))) + +(defun embark-target-heading-at-point () + "Target the outline heading at point." + (let ((beg (line-beginning-position)) + (end (line-end-position))) + (when (save-excursion + (goto-char beg) + (and (bolp) + (looking-at + ;; default definition from outline.el + (or (bound-and-true-p outline-regexp) "[*\^L]+")))) + (require 'outline) ;; Ensure that outline commands are available + `(heading ,(buffer-substring beg end) ,beg . ,end)))) + +(defun embark-target-text-heading-at-point () + "Target the outline heading at point in text modes." + (when (derived-mode-p 'text-mode) + (embark-target-heading-at-point))) + +(defun embark-target-prog-heading-at-point () + "Target the outline heading at point in programming modes." + (when (derived-mode-p 'prog-mode) + (embark-target-heading-at-point))) + +(defun embark-target-top-minibuffer-candidate () + "Target the top completion candidate in the minibuffer. +Return the category metadatum as the type of the target. + +This target finder is meant for the default completion UI and +completion UI highly compatible with it, like Icomplete. +Many completion UIs can still work with Embark but will need +their own target finder. See for example +`embark--vertico-selected'." + (when (and (minibufferp) minibuffer-completion-table) + (pcase-let* ((`(,category . ,candidates) (embark-minibuffer-candidates)) + (contents (minibuffer-contents)) + (top (if (test-completion contents + minibuffer-completion-table + minibuffer-completion-predicate) + contents + (let ((completions (completion-all-sorted-completions))) + (if (null completions) + contents + (concat + (substring contents + 0 (or (cdr (last completions)) 0)) + (car completions))))))) + (cons category (or (car (member top candidates)) top))))) + +(defun embark-target-collect-candidate () + "Target the collect candidate at point." + (when (derived-mode-p 'embark-collect-mode) + (when-let ((button + (pcase (get-text-property (point) 'tabulated-list-column-name) + ("Candidate" (button-at (point))) + ("Annotation" (previous-button (point))))) + (start (button-start button)) + (end (button-end button)) + (candidate (tabulated-list-get-id))) + `(,embark--type + ,(if (eq embark--type 'file) + (abbreviate-file-name (expand-file-name candidate)) + candidate) + ,start . ,end)))) + +(defun embark-target-completion-list-candidate () + "Return the completion candidate at point in a completions buffer." + (when (derived-mode-p 'completion-list-mode) + (if (not (get-text-property (point) 'mouse-face)) + (user-error "No completion here") + ;; this fairly delicate logic is taken from `choose-completion' + (let (beg end) + (cond + ((and (not (eobp)) (get-text-property (point) 'mouse-face)) + (setq end (point) beg (1+ (point)))) + ((and (not (bobp)) + (get-text-property (1- (point)) 'mouse-face)) + (setq end (1- (point)) beg (point))) + (t (user-error "No completion here"))) + (setq beg (previous-single-property-change beg 'mouse-face)) + (setq end (or (next-single-property-change end 'mouse-face) + (point-max))) + (let ((raw (or (get-text-property beg 'completion--string) + (buffer-substring beg end)))) + `(,embark--type + ,(if (eq embark--type 'file) + (abbreviate-file-name (expand-file-name raw)) + raw) + ,beg . ,end)))))) + +(defun embark--cycle-key () + "Return the key to use for `embark-cycle'." + (if embark-cycle-key + (if (key-valid-p embark-cycle-key) + (key-parse embark-cycle-key) + (error "`embark-cycle-key' is invalid")) + (car (where-is-internal #'embark-act)))) + +(defun embark--raw-action-keymap (type) + "Return raw action map for targets of given TYPE. +This does not take into account the default action, help key or +cycling bindings, just what's registered in +`embark-keymap-alist'." + (make-composed-keymap + (mapcar #'symbol-value + (let ((actions (or (alist-get type embark-keymap-alist) + (alist-get t embark-keymap-alist)))) + (ensure-list actions))))) + +(defun embark--action-keymap (type cycle) + "Return action keymap for targets of given TYPE. +If CYCLE is non-nil bind `embark-cycle'." + (make-composed-keymap + (let ((map (make-sparse-keymap)) + (default-action (embark--default-action type))) + (define-key map [13] default-action) + (when-let ((cycle-key (and cycle (embark--cycle-key)))) + (define-key map cycle-key #'embark-cycle)) + (when embark-help-key + (keymap-set map embark-help-key #'embark-keymap-help)) + map) + (embark--raw-action-keymap type))) + +(defun embark--truncate-target (target) + "Truncate TARGET string." + (unless (stringp target) + (setq target (format "%s" target))) + (if-let (pos (string-match-p "\n" target)) + (concat (car (split-string target "\n" 'omit-nulls "\\s-*")) "…") + target)) + +;;;###autoload +(defun embark-eldoc-first-target (report &rest _) + "Eldoc function reporting the first Embark target at point. +This function uses the eldoc REPORT callback and is meant to be +added to `eldoc-documentation-functions'." + (when-let (((not (minibufferp))) + (target (car (embark--targets)))) + (funcall report + (format "Embark on %s ‘%s’" + (plist-get target :type) + (embark--truncate-target (plist-get target :target)))))) + +;;;###autoload +(defun embark-eldoc-target-types (report &rest _) + "Eldoc function reporting the types of all Embark targets at point. +This function uses the eldoc REPORT callback and is meant to be +added to `eldoc-documentation-functions'." + (when-let (((not (minibufferp))) + (targets (embark--targets))) + (funcall report + (format "Embark target types: %s" + (mapconcat + (lambda (target) (symbol-name (plist-get target :type))) + targets + ", "))))) + +(defun embark--format-targets (target shadowed-targets rep) + "Return a formatted string indicating the TARGET of an action. + +This is used internally by the minimal indicator and for the +targets section of the verbose indicator. The string will also +mention any SHADOWED-TARGETS. A non-nil REP indicates we are in +a repeating sequence of actions." + (let ((act (propertize + (cond + ((plist-get target :multi) "∀ct") + (rep "Rep") + (t "Act")) + 'face 'highlight))) + (cond + ((eq (plist-get target :type) 'embark-become) + (propertize "Become" 'face 'highlight)) + ((and (minibufferp) + (not (eq 'embark-keybinding + (completion-metadata-get + (embark--metadata) + 'category)))) + ;; we are in a minibuffer but not from the + ;; completing-read prompter, use just "Act" + act) + ((plist-get target :multi) + (format "%s on %s %ss" + act + (plist-get target :multi) + (plist-get target :type))) + (t (format + "%s on %s%s ‘%s’" + act + (plist-get target :type) + (if shadowed-targets + (format (propertize "(%s)" 'face 'shadow) + (mapconcat + (lambda (target) (symbol-name (plist-get target :type))) + shadowed-targets + ", ")) + "") + (embark--truncate-target (plist-get target :target))))))) + +(defun embark-minimal-indicator () + "Minimal indicator, appearing in the minibuffer prompt or echo area. +This indicator displays a message showing the types of all +targets, starting with the current target, and the value of the +current target. The message is displayed in the echo area, or if +the minibuffer is open, the message is added to the prompt." + (lambda (&optional keymap targets _prefix) + (if (null keymap) + (when embark--minimal-indicator-overlay + (delete-overlay embark--minimal-indicator-overlay) + (setq-local embark--minimal-indicator-overlay nil)) + (let ((indicator (embark--format-targets + (car targets) (cdr targets) + (eq (lookup-key keymap [13]) #'embark-done)))) + (if (not (minibufferp)) + (message "%s" indicator) + (unless embark--minimal-indicator-overlay + (setq-local embark--minimal-indicator-overlay + (make-overlay (point-min) (point-min) + (current-buffer) t t))) + (overlay-put embark--minimal-indicator-overlay + 'before-string (concat indicator + (if (<= (length indicator) + (* 0.4 (frame-width))) + " " + "\n")))))))) + +(defun embark--read-key-sequence (update) + "Read key sequence, call UPDATE function with prefix keys." + (let (timer prefix) + (unwind-protect + (progn + (when (functionp update) + (setq timer (run-at-time + 0.05 0.05 + (lambda () + (let ((new-prefix (this-single-command-keys))) + (unless (equal prefix new-prefix) + (setq prefix new-prefix) + (when (/= (length prefix) 0) + (funcall update prefix)))))))) + (read-key-sequence-vector nil nil nil t 'cmd-loop)) + (when timer + (cancel-timer timer))))) + +(defvar embark-indicators) ; forward declaration + +(defun embark-keymap-prompter (keymap update) + "Let the user choose an action using the bindings in KEYMAP. +Besides the bindings in KEYMAP, the user is free to use all their +key bindings and even \\[execute-extended-command] to select a command. +UPDATE is the indicator update function." + (let* ((keys (let ((overriding-terminal-local-map keymap)) + (embark--read-key-sequence update))) + (cmd (let ((overriding-terminal-local-map keymap)) + (key-binding keys 'accept-default)))) + ;; Set last-command-event as it would be from the command loop. + ;; Previously we only set it locally for digit-argument and for + ;; the mouse scroll commands handled in this function. But other + ;; commands can need it too! For example, electric-pair-mode users + ;; may wish to bind ( to self-insert-command in embark-region-map. + ;; Also, as described in issue #402, there are circumstances where + ;; you might run consult-narrow through the embark-keymap-prompter. + (setq last-command-event (aref keys (1- (length keys)))) + (pcase cmd + ((or 'embark-keymap-help + (and 'nil ; cmd is nil but last key is help-char + (guard (eq help-char (aref keys (1- (length keys))))))) + (let ((embark-indicators + (cl-set-difference embark-indicators + '(embark-verbose-indicator + embark-mixed-indicator))) + (prefix-map + (if (eq cmd 'embark-keymap-help) + keymap + (let ((overriding-terminal-local-map keymap)) + (key-binding (seq-take keys (1- (length keys))) + 'accept-default)))) + (prefix-arg prefix-arg)) ; preserve prefix arg + (when-let ((win (get-buffer-window embark--verbose-indicator-buffer + 'visible))) + (quit-window 'kill-buffer win)) + (embark-completing-read-prompter prefix-map update))) + ((or 'universal-argument 'universal-argument-more + 'negative-argument 'digit-argument 'embark-toggle-quit) + ;; prevent `digit-argument' from modifying the overriding map + (let ((overriding-terminal-local-map overriding-terminal-local-map)) + (command-execute cmd)) + (embark-keymap-prompter + (make-composed-keymap universal-argument-map keymap) + update)) + ((or 'minibuffer-keyboard-quit 'abort-recursive-edit 'abort-minibuffers) + nil) + ((guard (let ((def (lookup-key keymap keys))) ; if directly + ; bound, then obey + (and def (not (numberp def))))) ; number means "invalid prefix" + cmd) + ((and (pred symbolp) + (guard (string-suffix-p "self-insert-command" (symbol-name cmd)))) + (minibuffer-message "Not an action") + (embark-keymap-prompter keymap update)) + ((or 'scroll-other-window 'scroll-other-window-down) + (let ((minibuffer-scroll-window + ;; NOTE: Here we special case the verbose indicator! + (or (get-buffer-window embark--verbose-indicator-buffer 'visible) + minibuffer-scroll-window))) + (ignore-errors (command-execute cmd))) + (embark-keymap-prompter keymap update)) + ((or 'scroll-bar-toolkit-scroll 'mwheel-scroll + 'mac-mwheel-scroll 'pixel-scroll-precision) + (funcall cmd last-command-event) + (embark-keymap-prompter keymap update)) + ('execute-extended-command + (let ((prefix-arg prefix-arg)) ; preserve prefix arg + (intern-soft (read-extended-command)))) + ((or 'keyboard-quit 'keyboard-escape-quit) + nil) + (_ cmd)))) + +(defun embark--command-name (cmd) + "Return an appropriate name for CMD. +If CMD is a symbol, use its symbol name; for lambdas, use the +first line of the documentation string; for keyboard macros use +`key-description'; otherwise use the word \"unnamed\"." + (concat ; fresh copy, so we can freely add text properties + (cond + ((or (stringp cmd) (vectorp cmd)) (key-description cmd)) + ((stringp (car-safe cmd)) (car cmd)) + ((eq (car-safe cmd) 'menu-item) (eval (cadr cmd))) + ((keymapp cmd) + (propertize (if (symbolp cmd) (format "+%s" cmd) "<keymap>") + 'face 'embark-keymap)) + ((symbolp cmd) + (let ((name (symbol-name cmd))) + (if (string-prefix-p "embark-action--" name) ; direct action mode + (format "(%s)" (string-remove-prefix "embark-action--" name)) + name))) + ((when-let (doc (and (functionp cmd) (ignore-errors (documentation cmd)))) + (save-match-data + (when (string-match "^\\(.*\\)$" doc) + (match-string 1 doc))))) + (t "<unnamed>")))) + +;; Taken from Marginalia, needed by the verbose indicator. +;; We cannot use the completion annotators in this case. +(defconst embark--advice-regexp + (rx bos + (1+ (seq (? "This function has ") + (or ":before" ":after" ":around" ":override" + ":before-while" ":before-until" ":after-while" + ":after-until" ":filter-args" ":filter-return") + " advice: " (0+ nonl) "\n")) + "\n") + "Regexp to match lines about advice in function documentation strings.") + +;; Taken from Marginalia, needed by the verbose indicator. +;; We cannot use the completion annotators in this case. +(defun embark--function-doc (sym) + "Documentation string of function SYM." + (let ((vstr (and (symbolp sym) (keymapp sym) (boundp sym) + (eq (symbol-function sym) (symbol-value sym)) + (documentation-property sym 'variable-documentation)))) + (when-let (str (or (ignore-errors (documentation sym)) vstr)) + ;; Replace standard description with variable documentation + (when (and vstr (string-match-p "\\`Prefix command" str)) + (setq str vstr)) + (save-match-data + (if (string-match embark--advice-regexp str) + (substring str (match-end 0)) + str))))) + +(defun embark--action-repeatable-p (action) + "Is ACTION repeatable? +When the return value is non-nil it will be the desired starting +point of the next target cycle or t to indicate the default, +namely that the target cycle for the next action should begin at +the type of the current target." + (or (cdr (assq action embark-repeat-actions)) + (and (memq action embark-repeat-actions) t))) + +(defun embark--formatted-bindings (keymap &optional nested) + "Return the formatted keybinding of KEYMAP. +The keybindings are returned in their order of appearance. +If NESTED is non-nil subkeymaps are not flattened." + (let* ((commands + (cl-loop for (key . def) in (embark--all-bindings keymap nested) + for name = (embark--command-name def) + for cmd = (keymap--menu-item-binding def) + unless (memq cmd '(nil embark-keymap-help + negative-argument digit-argument)) + collect (list name cmd key + (concat + (if (eq (car-safe def) 'menu-item) + "menu-item" + (key-description key)))))) + (width (cl-loop for (_name _cmd _key desc) in commands + maximize (length desc))) + (default) + (candidates + (cl-loop for item in commands + for (name cmd key desc) = item + for desc-rep = + (concat + (propertize desc 'face 'embark-keybinding) + (and (embark--action-repeatable-p cmd) + embark-keybinding-repeat)) + for formatted = + (propertize + (concat desc-rep + (make-string (- width (length desc-rep) -1) ?\s) + name) + 'embark-command cmd) + when (equal key [13]) + do (setq default formatted) + collect (cons formatted item)))) + (cons candidates default))) + +(defun embark--with-category (category candidates) + "Return completion table for CANDIDATES of CATEGORY with sorting disabled." + (lambda (string predicate action) + (if (eq action 'metadata) + `(metadata (display-sort-function . identity) + (cycle-sort-function . identity) + (category . ,category)) + (complete-with-action + action candidates string predicate)))) + +(defun embark-completing-read-prompter (keymap update &optional no-default) + "Prompt via completion for a command bound in KEYMAP. +If NO-DEFAULT is t, no default value is passed to`completing-read'. + +UPDATE is the indicator update function. It is not used directly +here, but if the user switches to `embark-keymap-prompter', the +UPDATE function is passed to it." + (let* ((candidates+def (embark--formatted-bindings keymap)) + (candidates (car candidates+def)) + (def (and (not no-default) (cdr candidates+def))) + (buf (current-buffer)) + (choice + (catch 'choice + (minibuffer-with-setup-hook + (lambda () + (let ((map (make-sparse-keymap))) + (define-key map "\M-q" + (lambda () + (interactive) + (with-current-buffer buf + (embark-toggle-quit)))) + (when-let (cycle (embark--cycle-key)) + ;; Rebind `embark-cycle' in order allow cycling + ;; from the `completing-read' prompter. Additionally + ;; `embark-cycle' can be selected via + ;; `completing-read'. The downside is that this breaks + ;; recursively acting on the candidates of type + ;; embark-keybinding in the `completing-read' prompter. + (define-key map cycle + (cond + ((eq (lookup-key keymap cycle) 'embark-cycle) + (lambda () + (interactive) + (throw 'choice 'embark-cycle))) + ((null embark-cycle-key) + (lambda () + (interactive) + (minibuffer-message + "No cycling possible; press `%s' again to act." + (key-description cycle)) + (define-key map cycle #'embark-act)))))) + (when embark-keymap-prompter-key + (keymap-set map embark-keymap-prompter-key + (lambda () + (interactive) + (message "Press key binding") + (let ((cmd (embark-keymap-prompter keymap update))) + (if (null cmd) + (user-error "Unknown key") + (throw 'choice cmd)))))) + (use-local-map + (make-composed-keymap map (current-local-map))))) + (completing-read + "Command: " + (embark--with-category 'embark-keybinding candidates) + nil nil nil 'embark--prompter-history def))))) + (pcase (assoc choice candidates) + (`(,_formatted ,_name ,cmd ,key ,_desc) + ;; Set last-command-event as it would be from the command loop. + (setq last-command-event (aref key (1- (length key)))) + cmd) + ('nil (intern-soft choice))))) + +;;; Verbose action indicator + +(defgroup embark-indicators nil + "Indicators display information about actions and targets." + :group 'embark) + +(defcustom embark-indicators + '(embark-mixed-indicator + embark-highlight-indicator + embark-isearch-highlight-indicator) + "Indicator functions to use when acting or becoming. +The indicator functions are called from both `embark-act' and +from `embark-become' and should display information about this to +the user, such as: which of those two commands is running; a +description of the key bindings that are available for actions or +commands to become; and, in the case of `embark-act', the type +and value of the targets, and whether other targets are available +via `embark-cycle'. The indicator function is free to display as +much or as little of this information as desired and can use any +Emacs interface elements to do so. + +Embark comes with five such indicators: + +- `embark-minimal-indicator', which does not display any + information about keybindings, but does display types and + values of action targets in the echo area or minibuffer prompt, + +- `embark-verbose-indicator', which pops up a buffer containing + detailed information including key bindings and the first line + of the docstring of the commands they run, and + +- `embark-mixed-indicator', which combines the minimal and the + verbose indicator: the minimal indicator is shown first and the + verbose popup is shown after `embark-mixed-indicator-delay' + seconds. + +- `embark-highlight-indicator', which highlights the target + at point. + +- `embark-isearch-highlight-indicator', which when the target at + point is an identifier or symbol, lazily highlights all + occurrences of it. + +The protocol for indicator functions is as follows: + +When called from `embark-act', an indicator function is called +without arguments. The indicator function should then return a +closure, which captures the indicator state. The returned +closure must accept up to three optional arguments, the action +keymap, the targets (plists as returned by `embark--targets') and +the prefix keys typed by the user so far. The keymap, targets +and prefix keys may be updated when cycling targets at point +resulting in multiple calls to the closure. When called from +`embark-become', the indicator closure will be called with the +keymap of commands to become, a fake target list containing a +single target of type `embark-become' and whose value is the +minibuffer input, and the prefix set to nil. Note, in +particular, that if an indicator function wishes to distinguish +between `embark-act' and `embark-become' it should check whether +the `car' of the first target is `embark-become'. + +After the action has been performed the indicator closure is +called without arguments, such that the indicator can perform the +necessary cleanup work. For example, if the indicator adds +overlays, it should remove these overlays. The indicator should +be written in a way that it is safe to call it for cleanup more +than once, in fact, it should be able to handle any sequence of +update and cleanup calls ending in a call for cleanup. + +NOTE: Experience shows that the indicator calling convention may +change again in order to support more action features. The +calling convention should currently be considered unstable. +Please keep this in mind when writing a custom indicator +function, or when using the `which-key' indicator function from +the wiki." + :type '(repeat + (choice + (const :tag "Verbose indicator" embark-verbose-indicator) + (const :tag "Minimal indicator" embark-minimal-indicator) + (const :tag "Mixed indicator" embark-mixed-indicator) + (const :tag "Highlight target" embark-highlight-indicator) + (const :tag "Highlight all occurrences" + embark-isearch-highlight-indicator) + (function :tag "Other")))) + +(defface embark-verbose-indicator-documentation + '((t :inherit completions-annotations)) + "Face used by the verbose action indicator to display binding descriptions. +Used by `embark-verbose-indicator'.") + +(defface embark-verbose-indicator-title '((t :height 1.1 :weight bold)) + "Face used by the verbose action indicator for the title. +Used by `embark-verbose-indicator'.") + +(defface embark-verbose-indicator-shadowed '((t :inherit shadow)) + "Face used by the verbose action indicator for the shadowed targets. +Used by `embark-verbose-indicator'.") + +(defcustom embark-verbose-indicator-display-action + '(display-buffer-reuse-window) + "Parameters added to `display-buffer-alist' to show the actions buffer. +See the docstring of `display-buffer' for information on what +display actions and parameters are available." + :type `(choice + (const :tag "Reuse some window" + (display-buffer-reuse-window)) + (const :tag "Below target buffer" + (display-buffer-below-selected + (window-height . fit-window-to-buffer))) + (const :tag "Bottom of frame (fixed-size)" + (display-buffer-at-bottom)) + (const :tag "Bottom of frame (resizes during cycling)" + (display-buffer-at-bottom + (window-height . fit-window-to-buffer))) + (const :tag "Side window on the right" + (display-buffer-in-side-window (side . right))) + (const :tag "Side window on the left" + (display-buffer-in-side-window (side . left))) + (sexp :tag "Other"))) + +(defcustom embark-verbose-indicator-excluded-actions nil + "Commands not displayed by `embark-verbose-indicator'. +This variable should be set to a list of symbols and regexps. +The verbose indicator will exclude from its listing any commands +matching an element of this list." + :type '(choice + (const :tag "Exclude nothing" nil) + (const :tag "Exclude Embark general actions" + (embark-collect embark-live embark-export + embark-cycle embark-act-all embark-keymap-help + embark-become embark-isearch-forward + embark-isearch-backward)) + (repeat :tag "Other" (choice regexp symbol)))) + +(defcustom embark-verbose-indicator-buffer-sections + `(target "\n" shadowed-targets " " cycle "\n" bindings) + "List of sections to display in the verbose indicator buffer, in order. +You can use either a symbol designating a concrete section (one +of the keywords below, but without the colon), a string literal +or a function returning a string or list of strings to insert and +that accepts the following keyword arguments: + +- `:target', the target as a cons of type and value, +- `:shadowed-targets', a list of conses for the other targets, +- `:bindings' a list returned by `embark--formatted-bindings', and +- `:cycle', a string describing the key binding of `embark-cycle'." + :type '(repeat + (choice (const :tag "Current target name" target) + (const :tag "List of other shadowed targets" shadowed-targets) + (const :tag "Key bindings" bindings) + (const :tag "Cycle indicator" cycle) + (string :tag "Literal string") + (function :tag "Custom function")))) + +(defcustom embark-verbose-indicator-nested t + "Whether the verbose indicator should use nested keymap navigation. +When this variable is non-nil the actions buffer displayed by +`embark-verbose-indicator' will include any prefix keys found in +the keymap it is displaying, and will update to show what is +bound under the prefix if the prefix is pressed. If this +variable is nil, then the actions buffer will contain a flat list +of all full key sequences bound in the keymap." + :type 'boolean) + +(defun embark--verbose-indicator-excluded-p (cmd) + "Return non-nil if CMD should be excluded from the verbose indicator." + (seq-find (lambda (x) + (if (symbolp x) + (eq cmd x) + (string-match-p x (symbol-name cmd)))) + embark-verbose-indicator-excluded-actions)) + +(cl-defun embark--verbose-indicator-section-target + (&key targets bindings &allow-other-keys) + "Format the TARGETS section for the indicator buffer. +BINDINGS is the formatted list of keybindings." + (let ((result (embark--format-targets + (car targets) + nil ; the shadowed targets section deals with these + (cl-find 'embark-done bindings :key #'caddr :test #'eq)))) + (add-face-text-property 0 (length result) + 'embark-verbose-indicator-title + 'append + result) + result)) + +(cl-defun embark--verbose-indicator-section-cycle + (&key cycle shadowed-targets &allow-other-keys) + "Format the CYCLE key section for the indicator buffer. +SHADOWED-TARGETS is the list of other targets." + (concat + (and cycle (propertize (format "(%s to cycle)" cycle) + 'face 'embark-verbose-indicator-shadowed)) + (and shadowed-targets "\n"))) + +(cl-defun embark--verbose-indicator-section-shadowed-targets + (&key shadowed-targets &allow-other-keys) + "Format the SHADOWED-TARGETS section for the indicator buffer." + (when shadowed-targets + (propertize (format "Shadowed targets at point: %s" + (string-join shadowed-targets ", ")) + 'face 'embark-verbose-indicator-shadowed))) + +(cl-defun embark--verbose-indicator-section-bindings + (&key bindings &allow-other-keys) + "Format the BINDINGS section for the indicator buffer." + (let* ((max-width (apply #'max (cons 0 (mapcar (lambda (x) + (string-width (car x))) + bindings)))) + (fmt (format "%%-%ds" (1+ max-width))) + (result nil)) + (dolist (binding bindings (string-join (nreverse result))) + (let ((cmd (caddr binding))) + (unless (embark--verbose-indicator-excluded-p cmd) + (let ((keys (format fmt (car binding))) + (doc (embark--function-doc cmd))) + (push (format "%s%s\n" keys + (propertize + (car (split-string (or doc "") "\n")) + 'face 'embark-verbose-indicator-documentation)) + result))))))) + +(defun embark--verbose-indicator-update (keymap targets) + "Update verbose indicator buffer. +The arguments are the new KEYMAP and TARGETS." + (with-current-buffer (get-buffer-create embark--verbose-indicator-buffer) + (let* ((inhibit-read-only t) + (bindings + (embark--formatted-bindings keymap embark-verbose-indicator-nested)) + (bindings (car bindings)) + (shadowed-targets (mapcar + (lambda (x) (symbol-name (plist-get x :type))) + (cdr targets))) + (cycle (let ((ck (where-is-internal #'embark-cycle keymap))) + (and ck (key-description (car ck)))))) + (setq-local cursor-type nil) + (setq-local truncate-lines t) + (setq-local buffer-read-only t) + (erase-buffer) + (dolist (section embark-verbose-indicator-buffer-sections) + (insert + (if (stringp section) + section + (or (funcall + (let ((prefixed (intern (format + "embark--verbose-indicator-section-%s" + section)))) + (cond + ((fboundp prefixed) prefixed) + ((fboundp section) section) + (t (error "Undefined verbose indicator section `%s'" + section)))) + :targets targets :shadowed-targets shadowed-targets + :bindings bindings :cycle cycle) + "")))) + (goto-char (point-min))))) + +(defun embark-verbose-indicator () + "Indicator that displays a table of key bindings in a buffer. +The default display includes the type and value of the current +target, the list of other target types, and a table of key +bindings, actions and the first line of their docstrings. + +The order and formatting of these items is completely +configurable through the variable +`embark-verbose-indicator-buffer-sections'. + +If the keymap being shown contains prefix keys, the table of key +bindings can either show just the prefixes and update once the +prefix is pressed, or it can contain a flat list of all full key +sequences bound in the keymap. This is controlled by the +variable `embark-verbose-indicator-nested'. + +To reduce clutter in the key binding table, one can set the +variable `embark-verbose-indicator-excluded-actions' to a list +of symbols and regexps matching commands to exclude from the +table. + +To configure how a window is chosen to display this buffer, see +the variable `embark-verbose-indicator-display-action'." + (lambda (&optional keymap targets prefix) + (if (not keymap) + (when-let ((win (get-buffer-window embark--verbose-indicator-buffer + 'visible))) + (quit-window 'kill-buffer win)) + (embark--verbose-indicator-update + (if (and prefix embark-verbose-indicator-nested) + ;; Lookup prefix keymap globally if not found in action keymap + (let ((overriding-terminal-local-map keymap)) + (key-binding prefix 'accept-default)) + keymap) + targets) + (let ((display-buffer-alist + `(,@display-buffer-alist + (,(regexp-quote embark--verbose-indicator-buffer) + ,@embark-verbose-indicator-display-action)))) + (display-buffer embark--verbose-indicator-buffer))))) + +(defcustom embark-mixed-indicator-delay 0.5 + "Time in seconds after which the verbose indicator is shown. +The mixed indicator starts by showing the minimal indicator and +after this delay shows the verbose indicator." + :type '(choice (const :tag "No delay" 0) + (number :tag "Delay in seconds"))) + +(defcustom embark-mixed-indicator-both nil + "Show both indicators, even after the verbose indicator appeared." + :type 'boolean) + +(defun embark-mixed-indicator () + "Mixed indicator showing keymap and targets. +The indicator shows the `embark-minimal-indicator' by default. +After `embark-mixed-indicator-delay' seconds, the +`embark-verbose-indicator' is shown. This which-key-like approach +ensures that Embark stays out of the way for quick actions. The +helpful keybinding reminder still pops up automatically without +further user intervention." + (let ((vindicator (embark-verbose-indicator)) + (mindicator (embark-minimal-indicator)) + vindicator-active + vtimer) + (lambda (&optional keymap targets prefix) + ;; Always cancel the timer. + ;; 1. When updating, cancel timer, since the user has pressed + ;; a key before the timer elapsed. + ;; 2. For cleanup, the timer must also be canceled. + (when vtimer + (cancel-timer vtimer) + (setq vtimer nil)) + (if (not keymap) + (progn + (funcall vindicator) + (when mindicator + (funcall mindicator))) + (when mindicator + (funcall mindicator keymap targets prefix)) + (if vindicator-active + (funcall vindicator keymap targets prefix) + (setq vtimer + (run-at-time + embark-mixed-indicator-delay nil + (lambda () + (when (and (not embark-mixed-indicator-both) mindicator) + (funcall mindicator) + (setq mindicator nil)) + (setq vindicator-active t) + (funcall vindicator keymap targets prefix))))))))) + +;;;###autoload +(defun embark-bindings-in-keymap (keymap) + "Explore command key bindings in KEYMAP with `completing-read'. +The selected command will be executed. Interactively, prompt the +user for a KEYMAP variable." + (interactive + (list + (symbol-value + (intern-soft + (completing-read + "Keymap: " + (embark--with-category + 'variable + (cl-loop for x being the symbols + if (and (boundp x) (keymapp (symbol-value x))) + collect (symbol-name x))) + nil t nil 'variable-name-history + (let ((major-mode-map + (concat (symbol-name major-mode) "-map"))) + (when (intern-soft major-mode-map) major-mode-map))))))) + (when-let (command (embark-completing-read-prompter keymap nil 'no-default)) + (call-interactively command))) + +;;;###autoload +(defun embark-bindings (global) + "Explore current command key bindings with `completing-read'. +The selected command will be executed. + +This shows key bindings from minor mode maps and the local +map (usually set by the major mode), but also less common keymaps +such as those from a text property or overlay, or the overriding +maps: `overriding-terminal-local-map' and `overriding-local-map'. + +Additionally, if GLOBAL is non-nil (interactively, if called with +a prefix argument), this command includes global key bindings." + (interactive "P") + (embark-bindings-in-keymap + (make-composed-keymap + (let ((all-maps (current-active-maps t))) + (if global all-maps (remq global-map all-maps)))))) + +;;;###autoload +(defun embark-bindings-at-point () + "Explore all key bindings at point with `completing-read'. +The selected command will be executed. + +This command lists key bindings found in keymaps specified by the +text properties `keymap' or `local-map', from either buffer text +or an overlay. These are not widely used in Emacs, and when they +are used can be somewhat hard to discover. Examples of locations +that have such a keymap are links and images in `eww' buffers, +attachment links in `gnus' article buffers, and the stash line +in a `vc-dir' buffer." + (interactive) + (if-let ((keymaps (delq nil (list (get-char-property (point) 'keymap) + (get-char-property (point) 'local-map))))) + (embark-bindings-in-keymap (make-composed-keymap keymaps)) + (user-error "No key bindings found at point"))) + +;;;###autoload +(defun embark-prefix-help-command () + "Prompt for and run a command bound in the prefix used for this command. +The prefix described consists of all but the last event of the +key sequence that ran this command. This function is intended to +be used as a value for `prefix-help-command'. + +In addition to using completion to select a command, you can also +type @ and the key binding (without the prefix)." + (interactive) + (when-let ((keys (this-command-keys-vector)) + (prefix (seq-take keys (1- (length keys)))) + (keymap (key-binding prefix 'accept-default))) + (minibuffer-with-setup-hook + (lambda () + (let ((pt (- (minibuffer-prompt-end) 2))) + (overlay-put (make-overlay pt pt) 'before-string + (format " under %s" (key-description prefix))))) + (embark-bindings-in-keymap keymap)))) + +(defun embark--prompt (indicators keymap targets) + "Call the prompter with KEYMAP and INDICATORS. +The TARGETS are displayed for actions outside the minibuffer." + (mapc (lambda (i) (funcall i keymap targets)) indicators) + (condition-case nil + (minibuffer-with-setup-hook + (lambda () + ;; if the prompter opens its own minibuffer, show + ;; the indicator there too + (let ((inner-indicators (mapcar #'funcall embark-indicators))) + (mapc (lambda (i) (funcall i keymap targets)) inner-indicators) + (add-hook 'minibuffer-exit-hook + (lambda () (mapc #'funcall inner-indicators)) + nil t))) + (let ((enable-recursive-minibuffers t)) + (funcall embark-prompter keymap + (lambda (prefix) + (mapc (lambda (i) (funcall i keymap targets prefix)) + indicators))))) + (quit nil))) + +(defvar embark--run-after-command-functions nil + "Abnormal hook, used by `embark--run-after-command'.") + +(defun embark--run-after-command (fn &rest args) + "Call FN with ARGS after the current commands finishes. +If multiple functions are queued with this function during the +same command, they will be called in the order from the one +queued most recently to the one queued least recently." + ;; We don't simply add FN to `post-command-hook' because FN may recursively + ;; call this function. In that case, FN would modify `post-command-hook' + ;; from within post-command-hook, which doesn't behave properly in our case. + ;; We use our own abnormal hook and run it from PCH in a way that it is OK to + ;; modify it from within its own functions. + (unless embark--run-after-command-functions + (let (pch timer has-run) + (setq pch + (lambda () + (remove-hook 'post-command-hook pch) + (cancel-timer timer) + (unless has-run + (setq has-run t) + (while embark--run-after-command-functions + ;; The following funcall may recursively call + ;; `embark--run-after-command', modifying + ;; `embark--run-after-command-functions'. This is why this + ;; loop has to be implemented carefully. We have to pop the + ;; function off the hook before calling it. Using `dolist' + ;; on the hook would also be incorrect, because it wouldn't + ;; take modifications of this hook into account. + (with-demoted-errors "embark PCH: %S" + (condition-case nil + (funcall (pop embark--run-after-command-functions)) + (quit (message "Quit")))))))) + (add-hook 'post-command-hook pch 'append) + ;; Generally we prefer `post-command-hook' because it plays well with + ;; keyboard macros. In some cases, `post-command-hook' isn't run after + ;; exiting a recursive edit, so set up the following timer as a backup. + (setq timer (run-at-time 0 nil pch)))) + + ;; Keep the default-directory alive, since this is often overwritten, + ;; for example by Consult commands. + ;; TODO it might be necessary to add more dynamically bound variables + ;; here. What we actually want are functions `capture-dynamic-scope' + ;; and `eval-in-dynamic-scope', but this does not exist? + (let ((dir default-directory)) + (push (lambda () + (let ((default-directory dir)) + (apply fn args))) + embark--run-after-command-functions))) + +(defun embark--quit-and-run (fn &rest args) + "Quit the minibuffer and then call FN with ARGS. +If called outside the minibuffer, simply apply FN to ARGS." + (if (not (minibufferp)) + (apply fn args) + (apply #'embark--run-after-command fn args) + (embark--run-after-command #'set 'ring-bell-function ring-bell-function) + (setq ring-bell-function #'ignore) + (if (fboundp 'minibuffer-quit-recursive-edit) + (minibuffer-quit-recursive-edit) + (abort-recursive-edit)))) + +(defun embark--run-action-hooks (hooks action target quit) + "Run HOOKS for ACTION. +The HOOKS argument must be alist. The keys t and :always are +treated specially. The :always hooks are executed always and the +t hooks are the default hooks, for when there are no +command-specific hooks for ACTION. The QUIT, ACTION and TARGET +arguments are passed to the hooks as keyword arguments." + (mapc (lambda (h) (apply h :action action :quit quit target)) + (or (alist-get action hooks) + (alist-get t hooks))) + (mapc (lambda (h) (apply h :action action :quit quit target)) + (alist-get :always hooks))) + +(defun embark--run-around-action-hooks + (action target quit &optional non-interactive) + "Run the `embark-around-action-hooks' for ACTION. +All the applicable around hooks are composed in the order they +are present in `embark-around-action-hooks'. The keys t and +:always in `embark-around-action-hooks' are treated specially. +The :always hooks are executed always (outermost) and the t hooks +are the default hooks, for when there are no command-specific +hooks for ACTION. The QUIT, ACTION and TARGET arguments are +passed to the hooks as keyword arguments. + +The optional argument NON-INTERACTIVE controls whether the action +is run with `command-execute' or with `funcall' passing the +target as argument." + (apply + (seq-reduce + (lambda (fn hook) + (lambda (&rest args) (apply hook (plist-put args :run fn)))) + (let ((hooks embark-around-action-hooks)) + (reverse + (append (or (alist-get action hooks) (alist-get t hooks)) + (alist-get :always hooks)))) + (if non-interactive + (lambda (&rest args) + (funcall (plist-get args :action) + (or (plist-get args :candidates) (plist-get args :target)))) + (lambda (&rest args) + (command-execute (plist-get args :action))))) + :action action :quit quit target)) + +(defun embark--act (action target &optional quit) + "Perform ACTION injecting the TARGET. +If called from a minibuffer with non-nil QUIT, quit the +minibuffer before executing the action." + (if (memq action '(embark-become ; these actions should run in + embark-collect ; the current buffer, not the + embark-live ; target buffer + embark-export + embark-select + embark-act-all)) + (progn + (embark--run-action-hooks embark-pre-action-hooks action target quit) + (unwind-protect (embark--run-around-action-hooks action target quit) + (embark--run-action-hooks embark-post-action-hooks + action target quit))) + (let* ((command embark--command) + (prefix prefix-arg) + (action-window (embark--target-window t)) + (directory default-directory) + (inject + (lambda () + (let ((contents (minibuffer-contents))) + (delete-minibuffer-contents) + (insert + (propertize + (substring-no-properties (plist-get target :target)) + 'embark--initial-input contents))) + (if (memq 'ivy--queue-exhibit post-command-hook) + ;; Ivy has special needs: (1) for file names + ;; ivy-immediate-done is not equivalent to + ;; exit-minibuffer, (2) it needs a chance to run + ;; its post command hook first, so use depth 10 + (add-hook 'post-command-hook 'ivy-immediate-done 10 t) + (add-hook 'post-command-hook #'exit-minibuffer nil t)) + (embark--run-action-hooks embark-target-injection-hooks + action target quit))) + (dedicate (and (derived-mode-p 'embark-collect-mode) + (not (window-dedicated-p)) + (selected-window))) + (multi (memq action embark-multitarget-actions)) + (run-action + (if (and (commandp action) (not multi)) + (lambda () + (let (final-window) + (when dedicate (set-window-dedicated-p dedicate t)) + (unwind-protect + (with-selected-window action-window + (let ((enable-recursive-minibuffers t) + (embark--command command) + (prefix-arg prefix) + ;; the next two avoid mouse dialogs + (use-dialog-box nil) + (last-nonmenu-event 13) + (default-directory directory)) + (embark--run-action-hooks embark-pre-action-hooks + action target quit) + (minibuffer-with-setup-hook inject + ;; pacify commands that use (this-command-keys) + (when (= (length (this-command-keys)) 0) + (set--this-command-keys + (if (characterp last-command-event) + (string last-command-event) + "\r"))) + (setq this-command action) + (embark--run-around-action-hooks + action target quit))) + (setq final-window (selected-window))) + (embark--run-action-hooks embark-post-action-hooks + action target quit) + (when dedicate (set-window-dedicated-p dedicate nil))) + (unless (eq final-window action-window) + (select-window final-window)))) + (let ((target + (if (and multi (null (plist-get target :candidates))) + (plist-put + target :candidates (list (plist-get target :target))) + target))) + (lambda () + (with-selected-window action-window + (embark--run-action-hooks embark-pre-action-hooks + action target quit) + (unwind-protect + (let ((current-prefix-arg prefix) + (default-directory directory)) + (embark--run-around-action-hooks + action target quit :non-interactive)) + (embark--run-action-hooks embark-post-action-hooks + action target quit)))))))) + (setq prefix-arg nil) + (if quit (embark--quit-and-run run-action) (funcall run-action))))) + +(defun embark--refine-multi-category (_type target) + "Refine `multi-category' TARGET to its actual type." + (or (let ((mc (get-text-property 0 'multi-category target))) + (cond + ;; The `cdr' of the `multi-category' property can be a buffer object. + ((and (eq (car mc) 'buffer) (buffer-live-p (cdr mc))) + (cons 'buffer (buffer-name (cdr mc)))) + ((stringp (cdr mc)) mc))) + (cons 'general target))) + +(defun embark--simplify-path (_type target) + "Simplify and '//' or '~/' in the TARGET file path." + (cons 'file (substitute-in-file-name target))) + +(defun embark--keybinding-command (_type target) + "Treat an `embark-keybinding' TARGET as a command." + (when-let ((cmd (get-text-property 0 'embark-command target))) + (cons 'command (format "%s" cmd)))) + +(defun embark--lookup-lighter-minor-mode (_type target) + "If TARGET is a lighter, look up its minor mode. + +The `describe-minor-mode' command has as completion candidates +both minor-modes and their lighters. This function replaces the +lighters by their minor modes, so actions expecting a function +work on them." + (cons 'minor-mode + (let ((symbol (intern-soft target))) + (if (and symbol (boundp symbol)) + target + (symbol-name (lookup-minor-mode-from-indicator target)))))) + +(declare-function project-current "project") +(declare-function project-roots "project") +(declare-function project-root "project") + +(defun embark--project-file-full-path (_type target) + "Get full path of project file TARGET." + ;; TODO project-find-file can be called from outside all projects in + ;; which case it prompts for a project first; we don't support that + ;; case yet, since there is no current project. + (cons 'file + (if-let ((project (project-current)) + (root (if (fboundp 'project-root) + (project-root project) + (with-no-warnings + (car (project-roots project)))))) + (expand-file-name target root) + target))) + +(defun embark--remove-package-version (_type target) + "Remove version number from a versioned package TARGET." + (cons 'package (replace-regexp-in-string "-[0-9.]+$" "" target))) + +(defun embark--targets () + "Retrieve current targets. + +An initial guess at the current targets and their types is +determined by running the functions in `embark-target-finders'. +Each function should either return nil, a pair of a type symbol +and target string or a triple of a type symbol, target string and +target bounds. + +In the minibuffer only the first target finder returning non-nil +is taken into account. When finding targets at point in other +buffers, all target finder functions are executed. + +For each target, the type is then looked up as a key in the +variable `embark-transformer-alist'. If there is a transformer +for the type, it is called with the type and target, and must +return a `cons' of the transformed type and transformed target. + +The return value of `embark--targets' is a list of plists. Each +plist concerns one target, and has keys `:type', `:target', +`:orig-type', `:orig-target' and `:bounds'." + (let (targets) + (run-hook-wrapped + 'embark-target-finders + (lambda (fun) + (dolist (found (when-let (result (funcall fun)) + (if (consp (car result)) result (list result)))) + (let* ((type (or (car found) 'general)) + (target+bounds (cdr found)) + (target (if (consp target+bounds) + (car target+bounds) + target+bounds)) + (bounds (and (consp target+bounds) (cdr target+bounds))) + (full-target + (append + (list :orig-type type :orig-target target :bounds bounds) + (if-let (transform (alist-get type embark-transformer-alist)) + (let ((trans (funcall transform type target))) + (list :type (car trans) :target (cdr trans))) + (list :type type :target target))))) + (push full-target targets))) + (and targets (minibufferp)))) + (nreverse + (cl-delete-duplicates ; keeps last duplicate, but we reverse + targets + :test (lambda (t1 t2) + (and (equal (plist-get t1 :target) (plist-get t2 :target)) + (eq (plist-get t1 :type) (plist-get t2 :type)))))))) + +(defun embark--default-action (type) + "Return default action for the given TYPE of target. +The most common case is that the target comes from minibuffer +completion, in which case the default action is the command that +opened the minibuffer in the first place. This can be overridden +by `embark-default-action-overrides'. + +For targets that do not come from minibuffer completion +\(typically some thing at point in a regular buffer) and whose +type is not listed in `embark-default-action-overrides', the +default action is given by whatever binding RET has in the action +keymap for the given type." + (or (alist-get (cons type embark--command) embark-default-action-overrides + nil nil #'equal) + (alist-get type embark-default-action-overrides) + (alist-get t embark-default-action-overrides) + embark--command + (lookup-key (embark--raw-action-keymap type) "\r"))) + +(defun embark--rotate (list k) + "Rotate LIST by K elements and return the rotated list." + (setq k (mod k (length list))) + (append (seq-drop list k) (seq-take list k))) + +(defun embark--orig-target (target) + "Convert TARGET to original target." + (plist-put + (plist-put + (copy-sequence target) + :target (plist-get target :orig-target)) + :type (plist-get target :orig-type))) + +(defun embark--quit-p (action) + "Determine whether to quit the minibuffer after ACTION. +This function consults `embark-quit-after-action' to decide +whether or not the user wishes to quit the minibuffer after +performing the ACTION, assuming this is done from a minibuffer." + (let* ((cfg embark-quit-after-action) + (quit (if (consp cfg) (alist-get action cfg (alist-get t cfg)) cfg))) + (when embark--toggle-quit (setq quit (not quit))) + (setq embark--toggle-quit nil) + quit)) + +;;;###autoload +(defun embark-act (&optional arg) + "Prompt the user for an action and perform it. +The targets of the action are chosen by `embark-target-finders'. +By default, if called from a minibuffer the target is the top +completion candidate. When called from a non-minibuffer buffer +there can multiple targets and you can cycle among them by using +`embark-cycle' (which is bound by default to the same key +binding `embark-act' is, but see `embark-cycle-key'). + +This command uses `embark-prompter' to ask the user to specify an +action, and calls it injecting the target at the first minibuffer +prompt. + +If you call this from the minibuffer, it can optionally quit the +minibuffer. The variable `embark-quit-after-action' controls +whether calling `embark-act' with nil ARG quits the minibuffer, +and if ARG is non-nil it will do the opposite. Interactively, +ARG is the prefix argument. + +If instead you call this from outside the minibuffer, the first +ARG targets are skipped over (if ARG is negative the skipping is +done by cycling backwards) and cycling starts from the following +target." + (interactive "P") + (let* ((targets (or (embark--targets) (user-error "No target found"))) + (indicators (mapcar #'funcall embark-indicators)) + (default-done nil)) + (when arg + (if (minibufferp) + (embark-toggle-quit) + (setq targets (embark--rotate targets (prefix-numeric-value arg))))) + (unwind-protect + (while + (let* ((target (car targets)) + (action + (or (embark--prompt + indicators + (let ((embark-default-action-overrides + (if default-done + `((t . ,default-done)) + embark-default-action-overrides))) + (embark--action-keymap (plist-get target :type) + (cdr targets))) + targets) + (user-error "Canceled"))) + (default-action (or default-done + (embark--default-action + (plist-get target :type))))) + (cond + ;; When acting twice in the minibuffer, do not restart + ;; `embark-act'. Otherwise the next `embark-act' will + ;; find a target in the original buffer. + ((eq action #'embark-act) + (message "Press an action key")) + ((eq action #'embark-cycle) + (setq targets (embark--rotate + targets (prefix-numeric-value prefix-arg)))) + (t + ;; if the action is non-repeatable, cleanup indicator now + (let ((repeat (embark--action-repeatable-p action))) + (unless repeat (mapc #'funcall indicators)) + (condition-case err + (embark--act + action + (if (and (eq action default-action) + (eq action embark--command) + (not (memq action embark-multitarget-actions))) + (embark--orig-target target) + target) + (embark--quit-p action)) + (user-error + (funcall (if repeat #'message #'user-error) + "%s" (cadr err)))) + (when-let (new-targets (and repeat (embark--targets))) + ;; Terminate repeated prompter on default action, + ;; when repeating. Jump to the region type if the + ;; region is active after the action, or else to the + ;; current type again. + (setq default-done #'embark-done + targets + (embark--rotate + new-targets + (or (cl-position-if + (let ((desired-type + (if (eq repeat t) + (plist-get (car targets) :type) + repeat))) + (lambda (x) + (eq (plist-get x :type) desired-type))) + new-targets) + 0))))))))) + (mapc #'funcall indicators)))) + +(defun embark--maybe-transform-candidates () + "Collect candidates and see if they all transform to the same type. +Return a plist with keys `:type', `:orig-type', `:candidates', and +`:orig-candidates'." + (pcase-let* ((`(,type . ,candidates) + (run-hook-with-args-until-success 'embark-candidate-collectors)) + (bounds (mapcar #'cdr-safe candidates))) + (setq candidates + (mapcar (lambda (x) (if (consp x) (car x) x)) candidates)) + (when (eq type 'file) + (let ((dir (embark--default-directory))) + (setq candidates + (mapcar (lambda (cand) + (abbreviate-file-name + (expand-file-name (substitute-in-file-name cand) dir))) + candidates)))) + ;; TODO more systematic approach to applying substitute-in-file-name + (append + (list :orig-type type :orig-candidates candidates :bounds bounds) + (or (when candidates + (when-let ((transformer (alist-get type embark-transformer-alist))) + (pcase-let* ((`(,new-type . ,first-cand) + (funcall transformer type (car candidates)))) + (let ((new-candidates (list first-cand))) + (when (cl-every + (lambda (cand) + (pcase-let ((`(,t-type . ,t-cand) + (funcall transformer type cand))) + (when (eq t-type new-type) + (push t-cand new-candidates) + t))) + (cdr candidates)) + (list :type new-type + :candidates (nreverse new-candidates))))))) + (list :type type :candidates candidates))))) + +;;;###autoload +(defun embark-act-all (&optional arg) + "Prompt the user for an action and perform it on each candidate. +The candidates are chosen by `embark-candidate-collectors'. By +default, if `embark-select' has been used to select some +candidates, then `embark-act-all' will act on those candidates; +otherwise, if the selection is empty and `embark-act-all' is +called from a minibuffer, then the candidates are the completion +candidates. + +This command uses `embark-prompter' to ask the user to specify an +action, and calls it injecting the target at the first minibuffer +prompt. + +If you call this from the minibuffer, it can optionally quit the +minibuffer. The variable `embark-quit-after-action' controls +whether calling `embark-act' with nil ARG quits the minibuffer, +and if ARG is non-nil it will do the opposite. Interactively, +ARG is the prefix argument." + (interactive "P") + (let* ((transformed (embark--maybe-transform-candidates)) + (type (plist-get transformed :type)) + (orig-type (plist-get transformed :orig-type)) + (candidates + (or (cl-mapcar + (lambda (cand orig-cand bounds) + (list :type type :target cand + :bounds (when bounds + (cons (copy-marker (car bounds)) + (copy-marker (cdr bounds)))) + :orig-type orig-type :orig-target orig-cand)) + (plist-get transformed :candidates) + (plist-get transformed :orig-candidates) + (plist-get transformed :bounds)) + (user-error "No candidates to act on"))) + (indicators (mapcar #'funcall embark-indicators))) + (when arg (embark-toggle-quit)) + (unwind-protect + (let* ((action + (or (embark--prompt + indicators (embark--action-keymap type nil) + (list (list :type type :multi (length candidates)))) + (user-error "Canceled"))) + (prefix prefix-arg) + (act (lambda (candidate) + (cl-letf (((symbol-function 'embark--restart) #'ignore) + ((symbol-function 'embark--confirm) #'ignore)) + (let ((prefix-arg prefix)) + (when-let ((bounds (plist-get candidate :bounds))) + (goto-char (car bounds))) + (embark--act action candidate))))) + (quit (embark--quit-p action))) + (when (and (eq action (embark--default-action type)) + (eq action embark--command)) + (setq candidates (mapcar #'embark--orig-target candidates))) + (when (or (not (or embark-confirm-act-all + (memq 'embark--confirm + (alist-get action embark-pre-action-hooks)))) + (y-or-n-p (format "Run %s on %d %ss? " + action (length candidates) type))) + (if (memq action embark-multitarget-actions) + (let ((prefix-arg prefix)) + (embark--act action transformed quit)) + (save-excursion + (if quit + (embark--quit-and-run #'mapc act candidates) + (mapc act candidates)))) + (when (and (not quit) + (memq 'embark--restart + (alist-get action embark-post-action-hooks))) + (embark--restart)))) + (dolist (cand candidates) + (when-let ((bounds (plist-get cand :bounds))) + (set-marker (car bounds) nil) ; yay, manual memory management! + (set-marker (cdr bounds) nil))) + (setq prefix-arg nil) + (mapc #'funcall indicators)))) + +(defun embark-highlight-indicator () + "Action indicator highlighting the target at point." + (let (overlay) + (lambda (&optional keymap targets _prefix) + (let ((bounds (plist-get (car targets) :bounds))) + (when (and overlay (or (not keymap) (not bounds))) + (delete-overlay overlay) + (setq overlay nil)) + (when bounds + (if overlay + (move-overlay overlay (car bounds) (cdr bounds)) + (setq overlay (make-overlay (car bounds) (cdr bounds))) + (overlay-put overlay 'category 'embark-target-overlay)) + (overlay-put overlay 'window (selected-window))))))) + +(defun embark-isearch-highlight-indicator () + "Action indicator highlighting all occurrences of the identifier at point. +This indicator only does something for targets which are +identifiers or symbols. For those it uses `isearch''s lazy +highlighting feature to highlight all occurrences of the target in +the buffer. This indicator is best used in conjunction with +`embark-highlight-indicator': by using them both you get the +target and the other occurrences of it highlighted in different +colors." + (lambda (&optional _keymap targets _prefix) + (if (and (not (minibufferp)) + (memq (plist-get (car targets) :orig-type) '(symbol identifier))) + (let ((isearch-string (plist-get (car targets) :target)) + (isearch-regexp-function #'isearch-symbol-regexp)) + (isearch-lazy-highlight-new-loop)) + (setq isearch-lazy-highlight-last-string nil) + (lazy-highlight-cleanup t)))) + +(defun embark-cycle (_arg) + "Cycle over the next ARG targets at point. +If ARG is negative, cycle backwards." + (interactive "p") + (user-error "Not meant to be called directly")) + +(defun embark-done () + "Terminate sequence of repeated actions." + (interactive)) + +;;;###autoload +(defun embark-dwim (&optional arg) + "Run the default action on the current target. +The target of the action is chosen by `embark-target-finders'. + +If the target comes from minibuffer completion, then the default +action is the command that opened the minibuffer in the first +place, unless overridden by `embark-default-action-overrides'. + +For targets that do not come from minibuffer completion +\(typically some thing at point in a regular buffer) and whose +type is not listed in `embark-default-action-overrides', the +default action is given by whatever binding RET has in the action +keymap for the target's type. + +See `embark-act' for the meaning of the prefix ARG." + (interactive "P") + (if-let ((targets (embark--targets))) + (let* ((target + (or (nth + (if (or (null arg) (minibufferp)) + 0 + (mod (prefix-numeric-value arg) (length targets))) + targets))) + (type (plist-get target :type)) + (default-action (embark--default-action type)) + (action (or (command-remapping default-action) default-action))) + (unless action + (user-error "No default action for %s targets" type)) + (when (and arg (minibufferp)) (setq embark--toggle-quit t)) + (embark--act action + (if (and (eq default-action embark--command) + (not (memq default-action + embark-multitarget-actions))) + (embark--orig-target target) + target) + (embark--quit-p action))) + (user-error "No target found"))) + +(defun embark--become-keymap () + "Return keymap of commands to become for current command." + (let ((map (make-composed-keymap + (cl-loop for keymap-name in embark-become-keymaps + for keymap = (symbol-value keymap-name) + when (where-is-internal embark--command (list keymap)) + collect keymap)))) + (when embark-help-key + (keymap-set map embark-help-key #'embark-keymap-help)) + map)) + +;;;###autoload +(defun embark-become (&optional full) + "Make current command become a different command. +Take the current minibuffer input as initial input for new +command. The new command can be run normally using key bindings or +\\[execute-extended-command], but if the current command is found in a keymap in +`embark-become-keymaps', that keymap is activated to provide +convenient access to the other commands in it. + +If FULL is non-nil (interactively, if called with a prefix +argument), the entire minibuffer contents are used as the initial +input of the new command. By default only the part of the +minibuffer contents between the current completion boundaries is +taken. What this means is fairly technical, but (1) usually +there is no difference: the completion boundaries include the +entire minibuffer contents, and (2) the most common case where +these notions differ is file completion, in which case the +completion boundaries single out the path component containing +point." + (interactive "P") + (unless (minibufferp) + (user-error "Not in a minibuffer")) + (let* ((target (embark--display-string ; remove invisible portions + (if full + (minibuffer-contents) + (pcase-let ((`(,beg . ,end) (embark--boundaries))) + (substring (minibuffer-contents) beg + (+ end (embark--minibuffer-point))))))) + (keymap (embark--become-keymap)) + (targets `((:type embark-become :target ,target))) + (indicators (mapcar #'funcall embark-indicators)) + (become (unwind-protect + (embark--prompt indicators keymap targets) + (mapc #'funcall indicators)))) + (unless become + (user-error "Canceled")) + (embark--become-command become target))) + +(defun embark--become-command (command input) + "Quit current minibuffer and start COMMAND with INPUT." + (embark--quit-and-run + (lambda () + (minibuffer-with-setup-hook + (lambda () + (delete-minibuffer-contents) + (insert input)) + (let ((use-dialog-box nil) ;; avoid mouse dialogs + (last-nonmenu-event 13)) + (setq this-command command) + (command-execute command)))))) + +;;; Embark collect + +(defgroup embark-collect nil + "Buffers for acting on collected Embark targets." + :group 'embark) + +(defcustom embark-candidate-collectors + '(embark-selected-candidates + embark-minibuffer-candidates + embark-completion-list-candidates + embark-dired-candidates + embark-ibuffer-candidates + embark-embark-collect-candidates + embark-custom-candidates) + "List of functions that collect all candidates in a given context. +These are used to fill an Embark Collect buffer. Each function +should return either nil (to indicate it found no candidates) or +a list whose first element is a symbol indicating the type of +candidates and whose `cdr' is the list of candidates, each of +which should be either a string or a dotted list of the +form (TARGET START . END), where START and END are the buffer +positions bounding the TARGET string." + :type 'hook) + +(defcustom embark-exporters-alist + '((buffer . embark-export-ibuffer) + (file . embark-export-dired) + (package . embark-export-list-packages) + (bookmark . embark-export-bookmarks) + (variable . embark-export-customize-variable) + (face . embark-export-customize-face) + (symbol . embark-export-apropos) + (minor-mode . embark-export-apropos) + (function . embark-export-apropos) + (command . embark-export-apropos) + (t . embark-collect)) + "Alist associating completion types to export functions. +Each function should take a list of strings which are candidates +for actions and make a buffer appropriate to manage them. For +example, the default is to make a Dired buffer for files, and an +ibuffer for buffers. + +The key t is also allowed in the alist, and the corresponding +value indicates the default function to use for other types. The +default is `embark-collect'" + :type '(alist :key-type symbol :value-type function)) + +(defcustom embark-after-export-hook nil + "Hook run after `embark-export' in the newly created buffer." + :type 'hook) + +(defface embark-collect-candidate '((t :inherit default)) + "Face for candidates in Embark Collect buffers.") + +(defface embark-collect-group-title + '((t :inherit shadow :slant italic)) + "Face for group titles in Embark Collect buffers.") + +(defface embark-collect-group-separator + '((t :inherit shadow :strike-through t italic)) + "Face for group titles in Embark Collect buffers.") + +(defcustom embark-collect-group-format + (concat + (propertize " " 'face 'embark-collect-group-separator) + (propertize " %s " 'face 'embark-collect-group-title) + (propertize " " 'face 'completions-group-separator + 'display '(space :align-to right))) + "Format string used for the group title in Embark Collect buffers." + :type 'string) + +(defface embark-collect-annotation '((t :inherit completions-annotations)) + "Face for annotations in Embark Collect. +This is only used for annotation that are not already fontified.") + +(defvar-local embark--rerun-function nil + "Function to rerun the collect or export that made the current buffer.") + +(autoload 'package-delete "package") +(declare-function package--from-builtin "package") +(declare-function package-desc-extras "package") +(declare-function package-desc-name "package") +(defvar package--builtins) +(defvar package-alist) +(defvar package-archive-contents) +(defvar package--initialized) + +(defun embark--package-desc (pkg) + "Return the description structure for package PKG." + (or ; found this in `describe-package-1' + (car (alist-get pkg package-alist)) + (if-let ((built-in (assq pkg package--builtins))) + (package--from-builtin built-in) + (car (alist-get pkg package-archive-contents))))) + +(defun embark-minibuffer-candidates () + "Return all current completion candidates from the minibuffer." + (when (minibufferp) + (let* ((all (completion-all-completions + (minibuffer-contents) + minibuffer-completion-table + minibuffer-completion-predicate + (embark--minibuffer-point))) + (last (last all))) + (when last (setcdr last nil)) + (cons + (completion-metadata-get (embark--metadata) 'category) + all)))) + +(defun embark-sorted-minibuffer-candidates () + "Return a sorted list of current minibuffer completion candidates. +This using the same sort order that `icomplete' and +`minibuffer-force-complete' use. The intended usage is that you +replace `embark-minibuffer-candidates' with this function in the +list `embark-candidate-collectors'." + (when (minibufferp) + (cons + (completion-metadata-get (embark--metadata) 'category) + (nconc (cl-copy-list (completion-all-sorted-completions)) nil)))) + +(declare-function dired-get-marked-files "dired") +(declare-function dired-move-to-filename "dired") +(declare-function dired-move-to-end-of-filename "dired") + +(defun embark-dired-candidates () + "Return marked or all files shown in Dired buffer. +If any buffer is marked, return marked buffers; otherwise, return +all buffers." + (when (derived-mode-p 'dired-mode) + (cons 'file + (or + ;; dired-get-marked-files returns the file on the current + ;; line if no marked files are found; and when the fourth + ;; argument is non-nil, the "no marked files" case is + ;; distinguished from the "single marked file" case by + ;; returning (list t marked-file) in the latter + (let ((marked (dired-get-marked-files t nil nil t))) + (and (cdr marked) + (if (eq (car marked) t) (cdr marked) marked))) + (save-excursion + (goto-char (point-min)) + (let (files) + (while (not (eobp)) + (when-let (file (dired-get-filename t t)) + (push `(,file + ,(progn (dired-move-to-filename) (point)) + . ,(progn (dired-move-to-end-of-filename t) (point))) + files)) + (forward-line)) + (nreverse files))))))) + +(autoload 'ibuffer-marked-buffer-names "ibuffer") +(declare-function ibuffer-map-lines-nomodify "ibuffer") + +(defun embark-ibuffer-candidates () + "Return marked or all buffers listed in ibuffer buffer. +If any buffer is marked, return marked buffers; otherwise, return +all buffers." + (when (derived-mode-p 'ibuffer-mode) + (cons 'buffer + (or (ibuffer-marked-buffer-names) + (let (buffers) + (ibuffer-map-lines-nomodify + (lambda (buffer _mark) + (push (buffer-name buffer) buffers))) + (nreverse buffers)))))) + +(defun embark-embark-collect-candidates () + "Return candidates in Embark Collect buffer. +This makes `embark-export' work in Embark Collect buffers." + (when (derived-mode-p 'embark-collect-mode) + (cons embark--type + (save-excursion + (goto-char (point-min)) + (let (all) + (when-let ((cand (embark-target-collect-candidate))) + (push (cdr cand) all)) + (while (forward-button 1 nil nil t) + (when-let ((cand (embark-target-collect-candidate))) + (push (cdr cand) all))) + (nreverse all)))))) + +(defun embark-completion-list-candidates () + "Return all candidates in a completions buffer." + (when (derived-mode-p 'completion-list-mode) + (cons + embark--type + (save-excursion + (goto-char (point-min)) + (next-completion 1) + (let (all) + (while (not (eobp)) + (push (cdr (embark-target-completion-list-candidate)) all) + (next-completion 1)) + (nreverse all)))))) + +(defun embark-custom-candidates () + "Return all variables and faces listed in this `Custom-mode' buffer." + (when (derived-mode-p 'Custom-mode) + (cons 'symbol ; gets refined to variable or face when acted upon + (save-excursion + (goto-char (point-min)) + (let (symbols) + (while (not (eobp)) + (when-let (widget (widget-at (point))) + (when (eq (car widget) 'custom-visibility) + (push + `(,(symbol-name + (plist-get (cdr (plist-get (cdr widget) :parent)) + :value)) + ,(point) + . ,(progn + (re-search-forward ":" (line-end-position) 'noerror) + (point))) + symbols))) + (forward-line)) + (nreverse symbols)))))) + + +(defun embark-collect--target () + "Return the Embark Collect candidate at point. +This takes into account `embark-transformer-alist'." + (let ((embark-target-finders '(embark-target-collect-candidate))) + (car (embark--targets)))) + +(defun embark--action-command (action) + "Turn an ACTION into a command to perform the action. +Returns the name of the command." + (let ((name (intern (format "embark-action--%s" + (embark--command-name action))))) + (fset name (lambda (arg) + (interactive "P") + (when-let (target (embark-collect--target)) + (let ((prefix-arg arg)) + (embark--act action target))))) + (when (fboundp action) + (put name 'function-documentation (documentation action))) + name)) + +(defun embark--all-bindings (keymap &optional nested) + "Return an alist of all bindings in KEYMAP. +If NESTED is non-nil subkeymaps are not flattened." + (let (bindings maps) + (map-keymap + (lambda (key def) + (cond + ((keymapp def) + (if nested + (push (cons (vector key) def) maps) + (dolist (bind (embark--all-bindings def)) + (push (cons (vconcat (vector key) (car bind)) (cdr bind)) + maps)))) + (def (push (cons (vector key) def) bindings)))) + (keymap-canonicalize keymap)) + (nconc (nreverse bindings) (nreverse maps)))) + +(defun embark-collect--direct-action-map (type) + "Return a direct action keymap for targets of given TYPE." + (let* ((actions (embark--action-keymap type nil)) + (map (make-sparse-keymap))) + (set-keymap-parent map button-map) + (pcase-dolist (`(,key . ,cmd) (embark--all-bindings actions)) + (unless (or (equal key [13]) + (memq cmd '(digit-argument negative-argument))) + (define-key map key (if (eq cmd 'embark-keymap-help) + #'embark-bindings-at-point + (embark--action-command cmd))))) + map)) + +(define-minor-mode embark-collect-direct-action-minor-mode + "Bind type-specific actions directly (without need for `embark-act')." + :init-value nil + :lighter " Act" + (unless (derived-mode-p 'embark-collect-mode) + (user-error "Not in an Embark Collect buffer")) + (save-excursion + (goto-char (point-min)) + (let ((inhibit-read-only t) maps) + (while (progn + (when (tabulated-list-get-id) + (put-text-property + (point) (button-end (point)) 'keymap + (if embark-collect-direct-action-minor-mode + (when-let ((target (embark-collect--target)) + (type (plist-get target :type))) + (or (alist-get type maps) + (setf (alist-get type maps) + (embark-collect--direct-action-map type))))))) + (forward-button 1 nil nil t)))))) + +(define-button-type 'embark-collect-entry + 'face 'embark-collect-candidate + 'action 'embark-collect-choose) + +(declare-function outline-toggle-children "outline") +(define-button-type 'embark-collect-group + 'face 'embark-collect-group-title + 'action (lambda (_) (outline-toggle-children))) + +(defun embark--boundaries () + "Get current minibuffer completion boundaries." + (let ((contents (minibuffer-contents)) + (pt (embark--minibuffer-point))) + (completion-boundaries + (substring contents 0 pt) + minibuffer-completion-table + minibuffer-completion-predicate + (substring contents pt)))) + +(defun embark-collect-choose (entry) + "Run default action on Embark Collect ENTRY." + (pcase-let ((`(,type ,text ,start . ,end) + (save-excursion + (goto-char entry) + (embark-target-collect-candidate)))) + (embark--act (embark--default-action type) + (list :target text + :type type + :bounds (cons start end))))) + +(defvar-keymap embark-collect-mode-map + :doc "Keymap for Embark collect mode." + :parent tabulated-list-mode-map + "a" #'embark-act + "A" #'embark-act-all + "M-a" #'embark-collect-direct-action-minor-mode + "E" #'embark-export + "s" #'isearch-forward + "n" #'forward-button + "p" #'backward-button + "}" 'outline-next-heading + "{" 'outline-previous-heading + "<remap> <forward-paragraph>" 'outline-next-heading + "<remap> <backward-paragraph>" 'outline-previous-heading + "<remap> <revert-buffer>" #'embark-rerun-collect-or-export) + +(defconst embark-collect--outline-string (string #x210000) + "Special string used for outline headings in Embark Collect buffers. +Chosen to be extremely unlikely to appear in a candidate.") + +(define-derived-mode embark-collect-mode tabulated-list-mode "Embark Collect" + "List of candidates to be acted on. +The command `embark-act' is bound `embark-collect-mode-map', but +you might prefer to change the key binding to match your other +key binding for it. Or alternatively you might want to enable the +embark collect direct action minor mode by adding the function +`embark-collect-direct-action-minor-mode' to +`embark-collect-mode-hook'. + +Reverting an Embark Collect buffer has slightly unusual behavior +if the buffer was obtained by running `embark-collect' from +within a minibuffer completion session. In that case reverting +just restarts the completion session, that is, the command that +opened the minibuffer is run again and the minibuffer contents +restored. You can then interact normally with the command, +perhaps editing the minibuffer contents, and, if you wish, you +can rerun `embark-collect' to get an updated buffer." + :interactive nil :abbrev-table nil :syntax-table nil) + +(defun embark-collect--metadatum (type metadatum) + "Get METADATUM for current buffer's candidates. +For non-minibuffers, assume candidates are of given TYPE." + (if (minibufferp) + (or (completion-metadata-get (embark--metadata) metadatum) + (plist-get completion-extra-properties + (intern (format ":%s" metadatum)))) + ;; otherwise fake some metadata for Marginalia users's benefit + (completion-metadata-get `((category . ,type)) metadatum))) + +(defun embark-collect--affixator (type) + "Get affixation function for current buffer's candidates. +For non-minibuffers, assume candidates are of given TYPE." + (or (embark-collect--metadatum type 'affixation-function) + (let ((annotator + (or (embark-collect--metadatum type 'annotation-function) + (lambda (_) "")))) + (lambda (candidates) + (mapcar (lambda (c) + (if-let (a (funcall annotator c)) (list c "" a) c)) + candidates))))) + +(defun embark--display-string (str) + ;; Note: Keep in sync with vertico--display-string + "Return display STR without display and invisible properties." + (let ((end (length str)) (pos 0) chunks) + (while (< pos end) + (let ((nextd (next-single-property-change pos 'display str end)) + (disp (get-text-property pos 'display str))) + (if (stringp disp) + (let ((face (get-text-property pos 'face str))) + (when face + (add-face-text-property + 0 (length disp) face t (setq disp (concat disp)))) + (setq pos nextd chunks (cons disp chunks))) + (while (< pos nextd) + (let ((nexti + (next-single-property-change pos 'invisible str nextd))) + (unless (or (get-text-property pos 'invisible str) + (and (= pos 0) (= nexti end))) ;; full=>no allocation + (push (substring str pos nexti) chunks)) + (setq pos nexti)))))) + (if chunks (apply #'concat (nreverse chunks)) str))) + +(defconst embark--hline + (propertize + (concat "\n" (propertize + " " 'display '(space :align-to right) + 'face '(:inherit completions-group-separator :height 0.01) + 'cursor-intangible t 'intangible t))) + "Horizontal line used to separate multiline collect entries.") + +(defun embark-collect--format-entries (candidates grouper) + "Format CANDIDATES for `tabulated-list-mode' grouped by GROUPER. +The GROUPER is either nil or a function like the `group-function' +completion metadatum, that is, a function of two arguments, the +first of which is a candidate and the second controls what is +computed: if nil, the title of the group the candidate belongs +to, and if non-nil, a rewriting of the candidate (useful to +simplify the candidate so it doesn't repeat the group title, for +example)." + (let ((max-width 0) + (transform + (if grouper (lambda (cand) (funcall grouper cand t)) #'identity))) + (setq + tabulated-list-entries + (mapcan + (lambda (group) + (let ((multiline (seq-some (lambda (x) (string-match-p "\n" (car x))) + candidates))) + (cons + `(nil [(,(concat (propertize embark-collect--outline-string + 'invisible t) + (format embark-collect-group-format (car group))) + type embark-collect-group) + ("" skip t)]) + (mapcar + (pcase-lambda (`(,cand ,prefix ,annotation)) + (let* ((display (embark--display-string (funcall transform cand))) + (length (length annotation)) + (faces (text-property-not-all + 0 length 'face nil annotation))) + (setq max-width (max max-width (+ (string-width prefix) + (string-width display)))) + (when faces + (add-face-text-property 0 length 'default t annotation)) + `(,cand + [(,(propertize + (if multiline (concat display embark--hline) display) + 'line-prefix prefix) + type embark-collect-entry) + (,annotation + skip t + ,@(unless faces + '(face embark-collect-annotation)))]))) + (cdr group))))) + (if grouper + (seq-group-by (lambda (item) (funcall grouper (car item) nil)) + candidates) + (list (cons "" candidates))))) + (if (null grouper) + (pop tabulated-list-entries) + (setq-local outline-regexp embark-collect--outline-string) + (outline-minor-mode)) + (setq tabulated-list-format + `[("Candidate" ,max-width t) ("Annotation" 0 t)]))) + +(defun embark-collect--update-candidates (buffer) + "Update candidates for Embark Collect BUFFER." + (let* ((transformed (embark--maybe-transform-candidates)) + (type (plist-get transformed :orig-type)) ; we need the originals for + (candidates (plist-get transformed :orig-candidates)) ; default action + (bounds (plist-get transformed :bounds)) + (affixator (embark-collect--affixator type)) + (grouper (embark-collect--metadatum type 'group-function))) + (when (eq type 'file) + (let ((dir (buffer-local-value 'default-directory buffer))) + (setq candidates + (mapcar (lambda (cand) + (let ((rel (file-relative-name cand dir))) + (if (string-prefix-p "../" rel) cand rel))) + candidates)))) + (if (seq-some #'identity bounds) + (cl-loop for cand in candidates and (start . _end) in bounds + when start + do (add-text-properties + 0 1 `(embark--location ,(copy-marker start)) cand))) + (setq candidates (funcall affixator candidates)) + (with-current-buffer buffer + (setq embark--type type) + (unless embark--command + (setq embark--command #'embark--goto)) + (embark-collect--format-entries candidates grouper)) + candidates)) + +(defun embark--goto (target) + "Jump to the original location of TARGET. +This function is used as a default action in Embark Collect +buffers when the candidates were a selection from a regular +buffer." + ;; TODO: ensure the location jumped to is visible + ;; TODO: remove duplication with embark-org-goto-heading + (when-let ((marker (get-text-property 0 'embark--location target))) + (pop-to-buffer (marker-buffer marker)) + (widen) + (goto-char marker) + (pulse-momentary-highlight-one-line))) + +(defun embark--collect (buffer-name) + "Create an Embark Collect buffer named BUFFER-NAME. + +The function `generate-new-buffer-name' is used to ensure the +buffer has a unique name." + (let ((buffer (generate-new-buffer buffer-name)) + (rerun (embark--rerun-function #'embark-collect))) + (with-current-buffer buffer + ;; we'll run the mode hooks once the buffer is displayed, so + ;; the hooks can make use of the window + (delay-mode-hooks (embark-collect-mode))) + + (embark--cache-info buffer) + (unless (embark-collect--update-candidates buffer) + (user-error "No candidates to collect")) + + (with-current-buffer buffer + (setq tabulated-list-use-header-line nil ; default to no header + header-line-format nil + tabulated-list--header-string nil) + (setq embark--rerun-function rerun)) + + (let ((window (display-buffer buffer))) + (with-selected-window window + (run-mode-hooks) + (tabulated-list-revert)) + (set-window-dedicated-p window t) + buffer))) + +(defun embark--descriptive-buffer-name (type) + "Return a descriptive name for an Embark collect or export buffer. +TYPE should be either `collect' or `export'." + (format "*Embark %s: %s*" + (capitalize (symbol-name type)) + (if (minibufferp) + (format "%s - %s" embark--command + (minibuffer-contents-no-properties)) + (buffer-name)))) + +;;;###autoload +(defun embark-collect () + "Create an Embark Collect buffer. + +To control the display, add an entry to `display-buffer-alist' +with key \"Embark Collect\". + +In Embark Collect buffers `revert-buffer' is remapped to +`embark-rerun-collect-or-export', which has slightly unusual +behavior if the buffer was obtained by running `embark-collect' +from within a minibuffer completion session. In that case +rerunning just restarts the completion session, that is, the +command that opened the minibuffer is run again and the +minibuffer contents restored. You can then interact normally with +the command, perhaps editing the minibuffer contents, and, if you +wish, you can rerun `embark-collect' to get an updated buffer." + (interactive) + (let ((buffer (embark--collect (embark--descriptive-buffer-name 'collect)))) + (when (minibufferp) + (embark--run-after-command #'pop-to-buffer buffer) + (embark--quit-and-run #'message nil)))) + +;;;###autoload +(defun embark-live () + "Create a live-updating Embark Collect buffer. + +To control the display, add an entry to `display-buffer-alist' +with key \"Embark Live\"." + (interactive) + (let ((live-buffer (embark--collect + (format "*Embark Live: %s*" + (if (minibufferp) + (format "M-x %s" embark--command) + (buffer-name))))) + (run-collect (make-symbol "run-collect")) + (stop-collect (make-symbol "stop-collect")) + timer) + (setf (symbol-function stop-collect) + (lambda () + (remove-hook 'change-major-mode-hook stop-collect t) + (remove-hook 'after-change-functions run-collect t))) + (setf (symbol-function run-collect) + (lambda (_1 _2 _3) + (unless timer + (setq timer + (run-with-idle-timer + 0.05 nil + (lambda () + (if (not (buffer-live-p live-buffer)) + (funcall stop-collect) + (embark-collect--update-candidates live-buffer) + (with-current-buffer live-buffer + ;; TODO figure out why I can't restore point + (tabulated-list-print t t)) + (setq timer nil)))))))) + (add-hook 'after-change-functions run-collect nil t) + (when (minibufferp) + (add-hook 'change-major-mode-hook stop-collect nil t)))) + +(defun embark--rerun-function (kind) + "Return a rerun function for an export or collect buffer in this context. +The parameter KIND should be either `embark-export' or `embark-collect'." + (let ((buffer (or embark--target-buffer (embark--target-buffer))) + (command embark--command)) + (cl-flet ((rerunner (action) + (lambda (&rest _) + (quit-window 'kill-buffer) + (with-current-buffer + (if (buffer-live-p buffer) buffer (current-buffer)) + (let ((embark--command command)) + (funcall action)))))) + (if (minibufferp) + (rerunner + (let ((input (minibuffer-contents-no-properties))) + (lambda () + (minibuffer-with-setup-hook + (lambda () + (delete-minibuffer-contents) + (insert input)) + (setq this-command embark--command) + (command-execute embark--command))))) + (rerunner kind))))) + +(defun embark-rerun-collect-or-export () + "Rerun the `embark-collect' or `embark-export' that created this buffer." + (interactive) + (if embark--rerun-function + (funcall embark--rerun-function) + (user-error "No function to rerun collect or export found"))) + +;;;###autoload +(defun embark-export () + "Create a type-specific buffer to manage current candidates. +The variable `embark-exporters-alist' controls how to make the +buffer for each type of completion. + +In Embark Export buffers `revert-buffer' is remapped to +`embark-rerun-collect-or-export', which has slightly unusual +behavior if the buffer was obtained by running `embark-export' +from within a minibuffer completion session. In that case +reverting just restarts the completion session, that is, the +command that opened the minibuffer is run again and the +minibuffer contents restored. You can then interact normally +with the command, perhaps editing the minibuffer contents, and, +if you wish, you can rerun `embark-export' to get an updated +buffer." + (interactive) + (let* ((transformed (embark--maybe-transform-candidates)) + (candidates (or (plist-get transformed :candidates) + (user-error "No candidates for export"))) + (type (plist-get transformed :type))) + (let ((exporter (or (alist-get type embark-exporters-alist) + (alist-get t embark-exporters-alist)))) + (if (eq exporter 'embark-collect) + (embark-collect) + (let* ((after embark-after-export-hook) + (cmd embark--command) + (name (embark--descriptive-buffer-name 'export)) + (rerun (embark--rerun-function #'embark-export)) + (buffer (save-excursion + (funcall exporter candidates) + (rename-buffer name t) + (current-buffer)))) + (embark--quit-and-run + (lambda () + (pop-to-buffer buffer) + (setq embark--rerun-function rerun) + (use-local-map + (make-composed-keymap + '(keymap + (remap keymap + (revert-buffer . embark-rerun-collect-or-export))) + (current-local-map))) + (let ((embark-after-export-hook after) + (embark--command cmd)) + (run-hooks 'embark-after-export-hook))))))))) + +(defmacro embark--export-rename (buffer title &rest body) + "Run BODY and rename BUFFER to Embark export buffer with TITLE." + (declare (indent 2)) + (let ((saved (make-symbol "saved"))) + `(let ((,saved (embark-rename-buffer + ,buffer " *Embark Saved*" t))) + ,@body + (set-buffer (embark-rename-buffer + ,buffer ,(format "*Embark Export %s*" title) t)) + (when ,saved (embark-rename-buffer ,saved ,buffer))))) + +(defun embark--export-customize (items type pred) + "Create a customization buffer listing ITEMS. +TYPE is the items type. +PRED is a predicate function used to filter the items." + (custom-buffer-create + (cl-loop for item in items + for sym = (intern-soft item) + when (and sym (funcall pred sym)) collect `(,sym ,type)))) + +(autoload 'apropos-parse-pattern "apropos") +(autoload 'apropos-symbols-internal "apropos") +(defun embark-export-apropos (symbols) + "Create apropos buffer listing SYMBOLS." + (embark--export-rename "*Apropos*" "Apropos" + (apropos-parse-pattern "") ;; Initialize apropos pattern + ;; HACK: Ensure that order of exported symbols is kept. + (cl-letf (((symbol-function #'sort) (lambda (list _pred) (nreverse list)))) + (apropos-symbols-internal + (delq nil (mapcar #'intern-soft symbols)) + (bound-and-true-p apropos-do-all))))) + +(defun embark-export-customize-face (faces) + "Create a customization buffer listing FACES." + (embark--export-customize faces 'custom-face #'facep)) + +(defun embark-export-customize-variable (variables) + "Create a customization buffer listing VARIABLES." + ;; The widget library serializes/deserializes the values. We advise + ;; the serialization in order to avoid errors for nonserializable + ;; variables. + (cl-letf* ((ht (make-hash-table :test #'equal)) + (orig-read (symbol-function #'read)) + (orig-write (symbol-function 'widget-sexp-value-to-internal)) + ((symbol-function #'read) + (lambda (&optional str) + (condition-case nil + (funcall orig-read str) + (error (gethash str ht))))) + ((symbol-function 'widget-sexp-value-to-internal) + (lambda (widget val) + (let ((str (funcall orig-write widget val))) + (puthash str val ht) + str)))) + (embark--export-customize variables 'custom-variable #'boundp))) + +(defun embark-export-ibuffer (buffers) + "Create an ibuffer buffer listing BUFFERS." + (ibuffer t "*Embark Export Ibuffer*" + `((predicate . (member (buffer-name) ',buffers))))) + +(autoload 'dired-check-switches "dired") +(declare-function dired-unadvertise "dired") +(defvar dired-directory) + +(defun embark-export-dired (files) + "Create a Dired buffer listing FILES." + (setq files (mapcar #'directory-file-name + (cl-remove-if-not #'file-exists-p files))) + (when (dired-check-switches dired-listing-switches "A" "almost-all") + (setq files (cl-remove-if + (lambda (path) + (let ((file (file-name-nondirectory path))) + (or (string= file ".") (string= file "..")))) + files))) + (cl-letf* ((dir (or (file-name-directory (try-completion "" files)) "")) + ;; Prevent reusing existing Dired buffer. + ((symbol-function 'dired-find-buffer-nocreate) #'ignore) + (buf (dired-noselect + (cons (expand-file-name dir) + (mapcar (lambda (file) (string-remove-prefix dir file)) + files))))) + (with-current-buffer buf + ;; Unadvertise to prevent the new buffer from being reused. + (dired-unadvertise default-directory) + (rename-buffer (format "*Embark Export Dired %s*" default-directory))) + (pop-to-buffer buf))) + +(autoload 'package-menu-mode "package") +(autoload 'package-menu--generate "package") + +(defun embark-export-list-packages (packages) + "Create a package menu mode buffer listing PACKAGES." + (let ((buf (generate-new-buffer "*Embark Export Packages*"))) + (with-current-buffer buf + (package-menu-mode) + (package-menu--generate nil (mapcar #'intern packages))) + (pop-to-buffer buf))) + +(defvar bookmark-alist) + +(defun embark-export-bookmarks (bookmarks) + "Create a `bookmark-bmenu-mode' buffer listing BOOKMARKS." + (embark--export-rename "*Bookmark List*" "Bookmarks" + (let ((bookmark-alist + (cl-remove-if-not + (lambda (bmark) + (member (car bmark) bookmarks)) + bookmark-alist))) + (bookmark-bmenu-list)))) + +;;; Multiple target selection + +(defface embark-selected '((t (:inherit match))) + "Face for selected candidates.") + +(defcustom embark-selection-indicator + #(" Embark:%s " 1 12 (face (embark-selected bold))) + "Mode line indicator used for selected candidates." + :type '(choice string (const nil))) + +(defvar-local embark--selection nil + "Buffer local list of selected targets. +Add or remove elements to this list using the `embark-select' +action.") + +(defun embark--selection-indicator () + "Mode line indicator showing number of selected items." + (when-let ((sel + (buffer-local-value + 'embark--selection + (or (when-let ((win (active-minibuffer-window))) + (window-buffer win)) + (current-buffer))))) + (format embark-selection-indicator (length sel)))) + +(cl-defun embark--select + (&key orig-target orig-type bounds &allow-other-keys) + "Add or remove ORIG-TARGET of given ORIG-TYPE to the selection. +If BOUNDS are given, also highlight the target when selecting it." + (cl-flet ((multi-type (x) (car (get-text-property 0 'multi-category x)))) + (if-let* ((existing (seq-find + (pcase-lambda (`(,cand . ,ov)) + (and + (equal cand orig-target) + (if (and bounds ov) + (and (= (car bounds) (overlay-start ov)) + (= (cdr bounds) (overlay-end ov))) + (let ((cand-type (multi-type cand))) + (or (eq cand-type orig-type) + (eq cand-type (multi-type orig-target))))))) + embark--selection))) + (progn + (when (cdr existing) (delete-overlay (cdr existing))) + (setq embark--selection (delq existing embark--selection))) + (let ((target (copy-sequence orig-target)) overlay) + (when bounds + (setq overlay (make-overlay (car bounds) (cdr bounds))) + (overlay-put overlay 'category 'embark-selected-overlay)) + (add-text-properties 0 (length orig-target) + `(multi-category ,(cons orig-type orig-target)) + target) + (push (cons target overlay) embark--selection)))) + (when embark-selection-indicator + (add-to-list 'mode-line-misc-info '(:eval (embark--selection-indicator))) + (force-mode-line-update t))) + +;;;###autoload +(defun embark-select () + "Add or remove the target from the current buffer's selection. +You can act on all selected targets at once with `embark-act-all'. +When called from outside `embark-act' this command will select +the first target at point." + (interactive) + (if-let ((target (car (embark--targets)))) + (apply #'embark--select target) + (user-error "No target to select"))) + +(defun embark-selected-candidates () + "Return currently selected candidates in the buffer." + (when embark--selection + (cl-flet ((unwrap (x) (get-text-property 0 'multi-category x))) + (let* ((first-type (car (unwrap (caar embark--selection)))) + (same (cl-every (lambda (item) + (eq (car (unwrap (car item))) first-type)) + embark--selection)) + (extract (if same + (pcase-lambda (`(,cand . ,overlay)) + (cons (cdr (unwrap cand)) overlay)) + #'identity))) + (cons + (if same first-type 'multi-category) + (nreverse + (mapcar + (lambda (item) + (pcase-let ((`(,cand . ,ov) (funcall extract item))) + (if ov `(,cand ,(overlay-start ov) . ,(overlay-end ov)) cand))) + embark--selection))))))) + +;;; Integration with external packages, mostly completion UIs + +;; marginalia + +;; Ensure that the Marginalia cache is reset, such that +;; `embark-toggle-variable-value' updates the display (See #540). +(with-eval-after-load 'marginalia + (push 'marginalia--cache-reset (alist-get :always embark-post-action-hooks))) + +;; vertico + +(declare-function vertico--candidate "ext:vertico") +(declare-function vertico--update "ext:vertico") +(defvar vertico--input) +(defvar vertico--candidates) +(defvar vertico--base) + +(defun embark--vertico-selected () + "Target the currently selected item in Vertico. +Return the category metadatum as the type of the target." + (when vertico--input + ;; Force candidate computation, if candidates are not yet available. + (vertico--update) + (cons (completion-metadata-get (embark--metadata) 'category) + (vertico--candidate)))) + +(defun embark--vertico-candidates () + "Collect the current Vertico candidates. +Return the category metadatum as the type of the candidates." + (when vertico--input + ;; Force candidate computation, if candidates are not yet available. + (vertico--update) + (cons (completion-metadata-get (embark--metadata) 'category) + vertico--candidates))) + +(defun embark--vertico-indicator () + "Embark indicator highlighting the current Vertico candidate." + (let ((fr face-remapping-alist)) + (lambda (&optional keymap _targets _prefix) + (when vertico--input + (setq-local face-remapping-alist + (if keymap + (cons '(vertico-current . embark-target) fr) + fr)))))) + +(with-eval-after-load 'vertico + (cl-defmethod vertico--format-candidate + :around (cand prefix suffix index start &context (embark--selection cons)) + (when (cl-find (concat vertico--base (nth index vertico--candidates)) + embark--selection + :test #'equal :key #'car) + (setq cand (copy-sequence cand)) + (add-face-text-property 0 (length cand) 'embark-selected t cand)) + (cl-call-next-method cand prefix suffix index start)) + (add-hook 'embark-indicators #'embark--vertico-indicator) + (add-hook 'embark-target-finders #'embark--vertico-selected) + (add-hook 'embark-candidate-collectors #'embark--vertico-candidates) + (remove-hook 'embark-candidate-collectors #'embark-selected-candidates) + (add-hook 'embark-candidate-collectors #'embark-selected-candidates)) + +;; ivy + +(declare-function ivy--expand-file-name "ext:ivy") +(declare-function ivy-state-current "ext:ivy") +(defvar ivy-text) +(defvar ivy-last) +(defvar ivy--old-cands) ; this stores the current candidates :) +(defvar ivy--length) + +(defun embark--ivy-selected () + "Target the currently selected item in Ivy. +Return the category metadatum as the type of the target." + ;; my favorite way of detecting Ivy + (when (memq 'ivy--queue-exhibit post-command-hook) + (cons + (completion-metadata-get (embark--metadata) 'category) + (ivy--expand-file-name + (if (and (> ivy--length 0) + (stringp (ivy-state-current ivy-last))) + (ivy-state-current ivy-last) + ivy-text))))) + +(defun embark--ivy-candidates () + "Return all current Ivy candidates." + ;; my favorite way of detecting Ivy + (when (memq 'ivy--queue-exhibit post-command-hook) + (cons + ;; swiper-isearch uses swiper-isearch-function as a completion + ;; table, but it doesn't understand metadata queries + (ignore-errors + (completion-metadata-get (embark--metadata) 'category)) + ivy--old-cands))) + +(with-eval-after-load 'ivy + (add-hook 'embark-target-finders #'embark--ivy-selected) + (add-hook 'embark-candidate-collectors #'embark--ivy-candidates) + (remove-hook 'embark-candidate-collectors #'embark-selected-candidates) + (add-hook 'embark-candidate-collectors #'embark-selected-candidates)) + +;;; Custom actions + +(defvar embark-separator-history nil + "Input history for the separators used by some embark commands. +The commands that prompt for a string separator are +`embark-insert' and `embark-copy-as-kill'.") + +(defun embark-keymap-help () + "Prompt for an action to perform or command to become and run it." + (interactive) + (user-error "Not meant to be called directly")) + +(defun embark-toggle-quit () + "Toggle whether the following action quits the minibuffer." + (interactive) + (when (minibufferp) + (setq embark--toggle-quit (not embark--toggle-quit)) + (if (consp embark-quit-after-action) + (message "Will %sobey embark-quit-after-action." + (if embark--toggle-quit "dis" "")) + (message + "Will %squit minibuffer after action" + (if (eq embark--toggle-quit embark-quit-after-action) "not " ""))))) + +(defun embark--separator (strings) + "Return a separator to join the STRINGS together. +With a prefix argument, prompt the user (unless STRINGS has 0 or +1 elements, in which case a separator is not needed)." + (if (and current-prefix-arg (cdr strings)) + (read-string "Separator: " nil 'embark-separator-history) + "\n")) + +(defun embark-copy-as-kill (strings) + "Join STRINGS and save on the `kill-ring'. +With a prefix argument, prompt for the separator to join the +STRINGS, which defaults to a newline." + (kill-new (string-join strings (embark--separator strings)))) + +(defun embark-insert (strings) + "Join STRINGS and insert the result at point. +With a prefix argument, prompt for the separator to join the +STRINGS, which defaults to a newline. + +Some whitespace is also inserted if necessary to avoid having the +inserted string blend into the existing buffer text. More +precisely: + +1. If the inserted string does not contain newlines, a space may +be added before or after it as needed to avoid inserting a word +constituent character next to an existing word constituent. + +2. For a multiline inserted string, newlines may be added before +or after as needed to ensure the inserted string is on lines of +its own." + (let* ((separator (embark--separator strings)) + (multiline + (or (and (cdr strings) (string-match-p "\n" separator)) + (and (null (cdr strings)) + (equal (buffer-substring (line-beginning-position) + (line-end-position)) + (car strings))) + (seq-some (lambda (s) (string-match-p "\n" s)) strings)))) + (cl-labels ((maybe-space () + (and (looking-at "\\w") (looking-back "\\w" 1) + (insert " "))) + (maybe-newline () + (or (looking-back "^[ \t]*" 40) (looking-at "\n") + (newline-and-indent))) + (maybe-whitespace () + (if multiline (maybe-newline) (maybe-space))) + (ins-string () + (let ((start (point))) + (insert + (mapconcat #'substring-no-properties strings separator)) + (save-excursion (goto-char start) (maybe-whitespace)) + (when (looking-back "\n" 1) (delete-char -1)) + (save-excursion (maybe-whitespace))))) + (if buffer-read-only + (with-selected-window (other-window-for-scrolling) + (ins-string)) + (ins-string))))) + +;; For Emacs 28 dired-jump will be moved to dired.el, but it seems +;; that since it already has an autoload in Emacs 28, this next +;; autoload is ignored. +(autoload 'dired-jump "dired-x" nil t) + +(defun embark-dired-jump (file &optional other-window) + "Open Dired buffer in directory containing FILE and move to its line. +When called with a prefix argument OTHER-WINDOW, open Dired in other window." + (interactive "fJump to Dired file: \nP") + (dired-jump other-window file)) + +(defun embark--read-from-history (prompt candidates &optional category) + "Read with completion from list of history CANDIDATES of CATEGORY. +Sorting and history are disabled. PROMPT is the prompt message." + (completing-read prompt + (embark--with-category category candidates) + nil t nil t)) + +(defun embark-kill-ring-remove (text) + "Remove TEXT from `kill-ring'." + (interactive (list (embark--read-from-history + "Remove from kill-ring: " kill-ring 'kill-ring))) + (embark-history-remove text) + (setq kill-ring (delete text kill-ring))) + +(defvar recentf-list) +(defun embark-recentf-remove (file) + "Remove FILE from the list of recent files." + (interactive (list (embark--read-from-history + "Remove recent file: " recentf-list 'file))) + (embark-history-remove (expand-file-name file)) + (embark-history-remove (abbreviate-file-name file)) + (when (and (boundp 'recentf-list) (fboundp 'recentf-expand-file-name)) + (setq recentf-list (delete (recentf-expand-file-name file) recentf-list)))) + +(defun embark-history-remove (str) + "Remove STR from `minibuffer-history-variable'. +Many completion UIs sort by history position. This command can be used +to remove entries from the history, such that they are not sorted closer +to the top." + (interactive (list (embark--read-from-history + "Remove history item: " + (if (eq minibuffer-history-variable t) + (user-error "No minibuffer history") + (symbol-value minibuffer-history-variable))))) + (unless (eq minibuffer-history-variable t) + (set minibuffer-history-variable + (delete str (symbol-value minibuffer-history-variable))))) + +(defvar xref-backend-functions) + +(defun embark-find-definition (symbol) + "Find definition of Emacs Lisp SYMBOL." + (interactive "sSymbol: ") + (let ((xref-backend-functions (lambda () 'elisp))) + (xref-find-definitions symbol))) + +(defun embark-info-lookup-symbol (symbol) + "Display the definition of SYMBOL, from the Elisp manual." + (interactive "SSymbol: ") + (info-lookup-symbol symbol 'emacs-lisp-mode)) + +(defun embark-rename-buffer (buffer newname &optional unique) + "Rename BUFFER to NEWNAME, optionally making it UNIQUE. +Interactively, you can set UNIQUE with a prefix argument. +Returns the new name actually used." + (interactive "bBuffer: \nBRename %s to: \nP") + (when-let ((buf (get-buffer buffer))) + (with-current-buffer buf + (rename-buffer newname unique)))) + +(defun embark--package-url (pkg) + "Return homepage for package PKG." + (when-let (desc (embark--package-desc pkg)) + (alist-get :url (package-desc-extras desc)))) + +(defun embark--prompt-for-package () + "Prompt user for a package name." + ;; this code is taken from the interactive spec of describe-package + (unless package--initialized + (package-initialize t)) + (intern + (completing-read "Package: " + (append (mapcar #'car package-alist) + (mapcar #'car package-archive-contents) + (mapcar #'car package--builtins))))) + +(defun embark-browse-package-url (pkg) + "Open homepage for package PKG with `browse-url'." + (interactive (list (embark--prompt-for-package))) + (if-let ((url (embark--package-url pkg))) + (browse-url url) + (user-error "No homepage found for `%s'" pkg))) + +(defun embark-save-package-url (pkg) + "Save URL of homepage for package PKG on the `kill-ring'." + (interactive (list (embark--prompt-for-package))) + (if-let ((url (embark--package-url pkg))) + (kill-new url) + (user-error "No homepage found for `%s'" pkg))) + +(defun embark-save-variable-value (var) + "Save value of VAR in the `kill-ring'." + (interactive "SVariable: ") + (kill-new (string-trim (pp-to-string (symbol-value var))))) + +(defun embark-insert-variable-value (var) + "Insert value of VAR." + (interactive "SVariable: ") + (embark-insert (list (string-trim (pp-to-string (symbol-value var)))))) + +(defun embark-toggle-variable (var &optional local) + "Toggle value of boolean variable VAR. +If prefix LOCAL is non-nil make variable local." + (interactive "SVariable: \nP") + (let ((val (symbol-value var))) + (unless (memq val '(nil t)) + (user-error "Not a boolean variable")) + (when local + (make-local-variable var)) + (funcall (or (get var 'custom-set) 'set) var (not val)))) + +(defun embark-insert-relative-path (file) + "Insert relative path to FILE. +The insert path is relative to `default-directory'." + (interactive "FFile: ") + (embark-insert (list (file-relative-name (substitute-in-file-name file))))) + +(defun embark-save-relative-path (file) + "Save the relative path to FILE in the kill ring. +The insert path is relative to `default-directory'." + (interactive "FFile: ") + (kill-new (file-relative-name (substitute-in-file-name file)))) + +(defun embark-shell-command-on-buffer (buffer command &optional replace) + "Run shell COMMAND on contents of BUFFER. +Called with \\[universal-argument], replace contents of buffer +with command output. For replacement behavior see +`shell-command-dont-erase-buffer' setting." + (interactive + (list + (read-buffer "Buffer: " nil t) + (read-shell-command "Shell command: ") + current-prefix-arg)) + (with-current-buffer buffer + (shell-command-on-region (point-min) (point-max) + command + (and replace (current-buffer))))) + +(defun embark-open-externally (file) + "Open FILE or url using system's default application." + (interactive "sOpen externally: ") + (unless (string-match-p "\\`[a-z]+://" file) + (setq file (expand-file-name file))) + (message "Opening `%s' externally..." file) + (if (and (eq system-type 'windows-nt) + (fboundp 'w32-shell-execute)) + (w32-shell-execute "open" file) + (call-process (pcase system-type + ('darwin "open") + ('cygwin "cygstart") + (_ "xdg-open")) + nil 0 nil file))) + +(declare-function bookmark-prop-get "bookmark") +(declare-function bookmark-completing-read "bookmark") + +(defun embark-bookmark-open-externally (bookmark) + "Open BOOKMARK in external application." + (interactive (list (bookmark-completing-read "Open externally: "))) + (embark-open-externally + (or (bookmark-prop-get bookmark 'location) + (bookmark-prop-get bookmark 'filename) + (user-error "Bookmark `%s' does not have a location" bookmark)))) + +(defun embark-bury-buffer (buf) + "Bury buffer BUF." + (interactive "bBuffer: ") + (if-let (win (get-buffer-window buf)) + (with-selected-window win + (bury-buffer)) + (bury-buffer))) + +(defun embark-kill-buffer-and-window (buf) + "Kill buffer BUF and delete its window." + (interactive "bBuffer: ") + (when-let (buf (get-buffer buf)) + (if-let (win (get-buffer-window buf)) + (with-selected-window win + (kill-buffer-and-window)) + (kill-buffer buf)))) + +(defun embark-save-unicode-character (char) + "Save Unicode character CHAR to kill ring." + (interactive + (list (read-char-by-name "Insert character (Unicode name or hex): "))) + (kill-new (format "%c" char))) + +(defun embark-isearch-forward () + "Prompt for string in the minibuffer and start isearch forwards. +Unlike isearch, this command reads the string from the +minibuffer, which means it can be used as an Embark action." + (interactive) + (isearch-mode t) + (isearch-edit-string)) + +(defun embark-isearch-backward () + "Prompt for string in the minibuffer and start isearch backwards. +Unlike isearch, this command reads the string from the +minibuffer, which means it can be used as an Embark action." + (interactive) + (isearch-mode nil) + (isearch-edit-string)) + +(defun embark-toggle-highlight () + "Toggle symbol highlighting using `highlight-symbol-at-point'." + (interactive) + (let ((regexp (find-tag-default-as-symbol-regexp)) + (highlighted (cl-find-if #'boundp + '(hi-lock-interactive-lighters + hi-lock-interactive-patterns)))) + (if (and highlighted (assoc regexp (symbol-value highlighted))) + (unhighlight-regexp regexp) + (highlight-symbol-at-point)))) + +(defun embark-next-symbol () + "Jump to next occurrence of symbol at point. +The search respects symbol boundaries." + (interactive) + (if-let ((symbol (thing-at-point 'symbol))) + (let ((regexp (format "\\_<%s\\_>" (regexp-quote symbol)))) + (when (looking-at regexp) + (forward-symbol 1)) + (unless (re-search-forward regexp nil t) + (user-error "Symbol `%s' not found" symbol))) + (user-error "No symbol at point"))) + +(defun embark-previous-symbol () + "Jump to previous occurrence of symbol at point. +The search respects symbol boundaries." + (interactive) + (if-let ((symbol (thing-at-point 'symbol))) + (let ((regexp (format "\\_<%s\\_>" (regexp-quote symbol)))) + (when (looking-back regexp (- (point) (length symbol))) + (forward-symbol -1)) + (unless (re-search-backward regexp nil t) + (user-error "Symbol `%s' not found" symbol))) + (user-error "No symbol at point"))) + +(defun embark-compose-mail (address) + "Compose email to ADDRESS." + ;; The only reason we cannot use compose-mail directly is its + ;; interactive specification, which just supplies nil for the + ;; address (and several other arguments). + (interactive "sTo: ") + (compose-mail address)) + +(autoload 'pp-display-expression "pp") + +(defun embark-pp-eval-defun (edebug) + "Run `eval-defun' and pretty print the result. +With a prefix argument EDEBUG, instrument the code for debugging." + (interactive "P") + (cl-letf (((symbol-function #'eval-expression-print-format) + (lambda (result) + (pp-display-expression result "*Pp Eval Output*")))) + (eval-defun edebug))) + +(defun embark-eval-replace (noquote) + "Evaluate region and replace with evaluated result. +If NOQUOTE is non-nil (interactively, if called with a prefix +argument), no quoting is used for strings." + (interactive "P") + (let ((beg (region-beginning)) + (end (region-end))) + (save-excursion + (goto-char end) + (insert (format (if noquote "%s" "%S") + (eval (read (buffer-substring beg end)) lexical-binding))) + (delete-region beg end)))) + +(when (< emacs-major-version 29) + (defun embark-elp-restore-package (prefix) + "Remove instrumentation from functions with names starting with PREFIX." + (interactive "SPrefix: ") + (when (fboundp 'elp-restore-list) + (elp-restore-list + (mapcar #'intern + (all-completions (symbol-name prefix) + obarray 'elp-profilable-p)))))) + +(defmacro embark--define-hash (algorithm) + "Define command which computes hash from a string. +ALGORITHM is the hash algorithm symbol understood by `secure-hash'." + `(defun ,(intern (format "embark-hash-%s" algorithm)) (str) + ,(format "Compute %s hash of STR and store it in the kill ring." algorithm) + (interactive "sString: ") + (let ((hash (secure-hash ',algorithm str))) + (kill-new hash) + (message "%s: %s" ',algorithm hash)))) + +(embark--define-hash md5) +(embark--define-hash sha1) +(embark--define-hash sha224) +(embark--define-hash sha256) +(embark--define-hash sha384) +(embark--define-hash sha512) + +(defun embark-encode-url (start end) + "Properly URI-encode the region between START and END in current buffer." + (interactive "r") + (let ((encoded (url-encode-url (buffer-substring-no-properties start end)))) + (delete-region start end) + (insert encoded))) + +(defun embark-decode-url (start end) + "Decode the URI-encoded region between START and END in current buffer." + (interactive "r") + (let ((decoded (url-unhex-string (buffer-substring-no-properties start end)))) + (delete-region start end) + (insert decoded))) + +(defvar epa-replace-original-text) +(defun embark-epa-decrypt-region (start end) + "Decrypt region between START and END." + (interactive "r") + (let ((epa-replace-original-text t)) + (epa-decrypt-region start end))) + +(defvar eww-download-directory) +(autoload 'eww-download-callback "eww") + +(defun embark-download-url (url) + "Download URL to `eww-download-directory'." + (interactive "sDownload URL: ") + (let ((dir eww-download-directory)) + (when (functionp dir) (setq dir (funcall dir))) + (access-file dir "Download failed") + (url-retrieve + url #'eww-download-callback + (if (>= emacs-major-version 28) (list url dir) (list url))))) + +;;; Setup and pre-action hooks + +(defun embark--restart (&rest _) + "Restart current command with current input. +Use this to refresh the list of candidates for commands that do +not handle that themselves." + (when (minibufferp) + (embark--become-command embark--command (minibuffer-contents)))) + +(defun embark--shell-prep (&rest _) + "Prepare target for use as argument for a shell command. +This quotes the spaces, inserts an extra space at the beginning +and leaves the point to the left of it." + (let ((contents (minibuffer-contents))) + (delete-minibuffer-contents) + (insert " " (shell-quote-wildcard-pattern contents)) + (goto-char (minibuffer-prompt-end)))) + +(defun embark--force-complete (&rest _) + "Select first minibuffer completion candidate matching target." + (minibuffer-force-complete)) + +(cl-defun embark--eval-prep (&key type &allow-other-keys) + "If target's TYPE is variable, skip edit; if function, wrap in ()." + (when (memq type '(command function)) + (embark--allow-edit) + (goto-char (minibuffer-prompt-end)) + (insert "(") + (goto-char (point-max)) + (insert ")") + (backward-char))) + +(cl-defun embark--beginning-of-target (&key bounds &allow-other-keys) + "Go to beginning of the target BOUNDS." + (when (number-or-marker-p (car bounds)) + (goto-char (car bounds)))) + +(cl-defun embark--end-of-target (&key bounds &allow-other-keys) + "Go to end of the target BOUNDS." + (when (number-or-marker-p (cdr bounds)) + (goto-char (cdr bounds)))) + +(cl-defun embark--mark-target (&rest rest &key run bounds &allow-other-keys) + "Mark the target if its BOUNDS are known. +After marking the target, call RUN with the REST of its arguments." + (cond + ((and bounds run) + (save-mark-and-excursion + (set-mark (cdr bounds)) + (goto-char (car bounds)) + (apply run :bounds bounds rest))) + (bounds ;; used as pre- or post-action hook + (set-mark (cdr bounds)) + (goto-char (car bounds))) + (run (apply run rest)))) + +(cl-defun embark--unmark-target (&rest _) + "Deactivate the region target." + (deactivate-mark t)) + +(cl-defun embark--narrow-to-target + (&rest rest &key run bounds &allow-other-keys) + "Narrow buffer to target if its BOUNDS are known. +Intended for use as an Embark around-action hook. This function +runs RUN with the buffer narrowed to given BOUNDS passing along +the REST of the arguments." + (if bounds + (save-excursion + (save-restriction + (narrow-to-region (car bounds) (cdr bounds)) + (goto-char (car bounds)) + (apply run :bounds bounds rest))) + (apply run rest))) + +(defun embark--allow-edit (&rest _) + "Allow editing the target." + (remove-hook 'post-command-hook #'exit-minibuffer t) + (remove-hook 'post-command-hook 'ivy-immediate-done t)) + +(defun embark--ignore-target (&rest _) + "Ignore the target." + (let ((contents + (get-text-property (minibuffer-prompt-end) 'embark--initial-input))) + (delete-minibuffer-contents) + (when contents (insert contents))) + (embark--allow-edit)) + +(autoload 'xref-push-marker-stack "xref") +(defun embark--xref-push-marker (&rest _) + "Push a marker onto the xref marker stack." + (xref-push-marker-stack)) + +(cl-defun embark--confirm (&key action target &allow-other-keys) + "Ask for confirmation before running the ACTION on the TARGET." + (unless (y-or-n-p (format "Run %s on %s? " action target)) + (user-error "Canceled"))) + +(defconst embark--associated-file-fn-alist + `((file . identity) + (buffer . ,(lambda (target) + (let ((buffer (get-buffer target))) + (or (buffer-file-name buffer) + (buffer-local-value 'default-directory buffer))))) + (bookmark . bookmark-location) + (library . locate-library)) + "Alist of functions that extract a file path from targets of a given type.") + +(defun embark--associated-directory (target type) + "Return directory associated to TARGET of given TYPE. +The supported values of TYPE are file, buffer, bookmark and +library, which have an obvious notion of associated directory." + (when-let ((file-fn (alist-get type embark--associated-file-fn-alist)) + (file (funcall file-fn target))) + (if (file-directory-p file) + (file-name-as-directory file) + (file-name-directory file)))) + +(cl-defun embark--cd (&rest rest &key run target type &allow-other-keys) + "Run action with `default-directory' set to the directory of TARGET. +The supported values of TYPE are file, buffer, bookmark and +library, which have an obvious notion of associated directory. +The REST of the arguments are also passed to RUN." + (let ((default-directory + (or (embark--associated-directory target type) default-directory))) + (apply run :target target :type type rest))) + +(cl-defun embark--save-excursion (&rest rest &key run &allow-other-keys) + "Run action without moving point. +This simply calls RUN with the REST of its arguments inside +`save-excursion'." + (save-excursion (apply run rest))) + +(defun embark--universal-argument (&rest _) + "Run action with a universal prefix argument." + (setq prefix-arg '(4))) + +;;; keymaps + +(defvar-keymap embark-meta-map + :doc "Keymap for non-action Embark functions." + "-" #'negative-argument + "0" #'digit-argument + "1" #'digit-argument + "2" #'digit-argument + "3" #'digit-argument + "4" #'digit-argument + "5" #'digit-argument + "6" #'digit-argument + "7" #'digit-argument + "8" #'digit-argument + "9" #'digit-argument) + +(defvar-keymap embark-general-map + :doc "Keymap for Embark general actions." + :parent embark-meta-map + "i" #'embark-insert + "w" #'embark-copy-as-kill + "q" #'embark-toggle-quit + "E" #'embark-export + "S" #'embark-collect + "L" #'embark-live + "B" #'embark-become + "A" #'embark-act-all + "C-s" #'embark-isearch-forward + "C-r" #'embark-isearch-backward + "C-SPC" #'mark + "DEL" #'delete-region + "SPC" #'embark-select) + +(defvar-keymap embark-encode-map + :doc "Keymap for Embark region encoding actions." + "r" #'rot13-region + "." #'morse-region + "-" #'unmorse-region + "s" #'studlify-region + "m" #'embark-hash-md5 + "1" #'embark-hash-sha1 + "2" #'embark-hash-sha256 + "3" #'embark-hash-sha384 + "4" #'embark-hash-sha224 + "5" #'embark-hash-sha512 + "f" #'format-encode-region + "F" #'format-decode-region + "b" #'base64-encode-region + "B" #'base64-decode-region + "u" #'embark-encode-url + "U" #'embark-decode-url + "c" #'epa-encrypt-region + "C" #'embark-epa-decrypt-region) + +(fset 'embark-encode-map embark-encode-map) + +(defvar-keymap embark-sort-map + :doc "Keymap for Embark actions that sort the region" + "l" #'sort-lines + "P" #'sort-pages + "f" #'sort-fields + "c" #'sort-columns + "p" #'sort-paragraphs + "r" #'sort-regexp-fields + "n" #'sort-numeric-fields) + +(fset 'embark-sort-map embark-sort-map) + +;; these will have autoloads in Emacs 28 +(autoload 'calc-grab-sum-down "calc" nil t) +(autoload 'calc-grab-sum-across "calc" nil t) + +;; this has had an autoload cookie since at least Emacs 26 +;; but that autoload doesn't seem to work for me +(autoload 'org-table-convert-region "org-table" nil t) + +(defvar-keymap embark-region-map + :doc "Keymap for Embark actions on the active region." + :parent embark-general-map + "u" #'upcase-region + "l" #'downcase-region + "c" #'capitalize-region + "|" #'shell-command-on-region + "e" #'eval-region + "<" #'embark-eval-replace + "a" #'align + "A" #'align-regexp + "<left>" #'indent-rigidly + "<right>" #'indent-rigidly + "TAB" #'indent-region + "f" #'fill-region + "p" #'fill-region-as-paragraph + "$" #'ispell-region + "=" #'count-words-region + "F" #'whitespace-cleanup-region + "t" #'transpose-regions + "o" #'org-table-convert-region + ";" #'comment-or-uncomment-region + "W" #'write-region + "+" #'append-to-file + "m" #'apply-macro-to-region-lines + "n" #'narrow-to-region + "*" #'calc-grab-region + ":" #'calc-grab-sum-down + "_" #'calc-grab-sum-across + "r" #'reverse-region + "d" #'delete-duplicate-lines + "b" #'browse-url-of-region + "h" #'shr-render-region + "'" #'expand-region-abbrevs + "v" #'vc-region-history + "R" #'repunctuate-sentences + "s" 'embark-sort-map + ">" 'embark-encode-map) + +(defvar-keymap embark-vc-file-map + :doc "Keymap for Embark VC file actions." + "d" #'vc-delete-file + "r" #'vc-rename-file + "i" #'vc-ignore) + +(fset 'embark-vc-file-map embark-vc-file-map) + +(defvar-keymap embark-file-map + :doc "Keymap for Embark file actions." + :parent embark-general-map + "RET" #'find-file + "f" #'find-file + "F" #'find-file-literally + "o" #'find-file-other-window + "d" #'delete-file + "D" #'delete-directory + "r" #'rename-file + "c" #'copy-file + "s" #'make-symbolic-link + "j" #'embark-dired-jump + "!" #'shell-command + "&" #'async-shell-command + "$" #'eshell + "<" #'insert-file + "m" #'chmod + "=" #'ediff-files + "+" #'make-directory + "\\" #'embark-recentf-remove + "I" #'embark-insert-relative-path + "W" #'embark-save-relative-path + "x" #'embark-open-externally + "e" #'eww-open-file + "l" #'load-file + "b" #'byte-compile-file + "R" #'byte-recompile-directory + "v" 'embark-vc-file-map) + +(defvar-keymap embark-kill-ring-map + :doc "Keymap for `kill-ring' commands." + :parent embark-general-map + "\\" #'embark-kill-ring-remove) + +(defvar-keymap embark-url-map + :doc "Keymap for Embark url actions." + :parent embark-general-map + "RET" #'browse-url + "b" #'browse-url + "d" #'embark-download-url + "x" #'embark-open-externally + "e" #'eww) + +(defvar-keymap embark-email-map + :doc "Keymap for Embark email actions." + :parent embark-general-map + "RET" #'embark-compose-mail + "c" #'embark-compose-mail) + +(defvar-keymap embark-library-map + :doc "Keymap for operations on Emacs Lisp libraries." + :parent embark-general-map + "RET" #'find-library + "l" #'load-library + "f" #'find-library + "h" #'finder-commentary + "a" #'apropos-library + "L" #'locate-library + "m" #'info-display-manual + "$" #'eshell) + +(defvar-keymap embark-buffer-map + :doc "Keymap for Embark buffer actions." + :parent embark-general-map + "RET" #'switch-to-buffer + "k" #'kill-buffer + "b" #'switch-to-buffer + "o" #'switch-to-buffer-other-window + "z" #'embark-bury-buffer + "K" #'embark-kill-buffer-and-window + "r" #'embark-rename-buffer + "=" #'ediff-buffers + "|" #'embark-shell-command-on-buffer + "<" #'insert-buffer + "$" #'eshell) + +(defvar-keymap embark-tab-map + :doc "Keymap for actions for tab-bar tabs." + :parent embark-general-map + "RET" #'tab-bar-select-tab-by-name + "s" #'tab-bar-select-tab-by-name + "r" #'tab-bar-rename-tab-by-name + "k" #'tab-bar-close-tab-by-name) + +(defvar-keymap embark-identifier-map + :doc "Keymap for Embark identifier actions." + :parent embark-general-map + "RET" #'xref-find-definitions + "h" #'display-local-help + "H" #'embark-toggle-highlight + "d" #'xref-find-definitions + "r" #'xref-find-references + "a" #'xref-find-apropos + "s" #'info-lookup-symbol + "n" #'embark-next-symbol + "p" #'embark-previous-symbol + "'" #'expand-abbrev + "$" #'ispell-word + "o" #'occur) + +(defvar-keymap embark-expression-map + :doc "Keymap for Embark expression actions." + :parent embark-general-map + "RET" #'pp-eval-expression + "e" #'pp-eval-expression + "<" #'embark-eval-replace + "m" #'pp-macroexpand-expression + "TAB" #'indent-region + "r" #'raise-sexp + ";" #'comment-dwim + "t" #'transpose-sexps + "k" #'kill-region + "u" #'backward-up-list + "n" #'forward-list + "p" #'backward-list) + +(defvar-keymap embark-defun-map + :doc "Keymap for Embark defun actions." + :parent embark-expression-map + "RET" #'embark-pp-eval-defun + "e" #'embark-pp-eval-defun + "c" #'compile-defun + "D" #'edebug-defun + "o" #'checkdoc-defun + "N" #'narrow-to-defun) + +;; Use quoted symbols to avoid byte-compiler warnings. +(defvar-keymap embark-heading-map + :doc "Keymap for Embark heading actions." + :parent embark-general-map + "RET" 'outline-show-subtree + "TAB" 'outline-cycle ;; New in Emacs 28! + "C-SPC" 'outline-mark-subtree + "n" 'outline-next-visible-heading + "p" 'outline-previous-visible-heading + "f" 'outline-forward-same-level + "b" 'outline-backward-same-level + "^" 'outline-move-subtree-up + "v" 'outline-move-subtree-down + "u" 'outline-up-heading + "+" 'outline-show-subtree + "-" 'outline-hide-subtree + ">" 'outline-demote + "<" 'outline-promote) + +(defvar-keymap embark-symbol-map + :doc "Keymap for Embark symbol actions." + :parent embark-identifier-map + "RET" #'embark-find-definition + "h" #'describe-symbol + "s" #'embark-info-lookup-symbol + "d" #'embark-find-definition + "e" #'pp-eval-expression + "a" #'apropos + "\\" #'embark-history-remove) + +(defvar-keymap embark-face-map + :doc "Keymap for Embark face actions." + :parent embark-symbol-map + "h" #'describe-face + "c" #'customize-face + "+" #'make-face-bold + "-" #'make-face-unbold + "/" #'make-face-italic + "|" #'make-face-unitalic + "!" #'invert-face + "f" #'set-face-foreground + "b" #'set-face-background) + +(defvar-keymap embark-variable-map + :doc "Keymap for Embark variable actions." + :parent embark-symbol-map + "=" #'set-variable + "c" #'customize-set-variable + "u" #'customize-variable + "v" #'embark-save-variable-value + "<" #'embark-insert-variable-value + "t" #'embark-toggle-variable) + +(defvar-keymap embark-function-map + :doc "Keymap for Embark function actions." + :parent embark-symbol-map + "m" #'elp-instrument-function ;; m=measure + "M" 'elp-restore-function ;; quoted, not autoloaded + "k" #'debug-on-entry ;; breaKpoint (running out of letters, really) + "K" #'cancel-debug-on-entry + "t" #'trace-function + "T" 'untrace-function) ;; quoted, not autoloaded + +(defvar-keymap embark-command-map + :doc "Keymap for Embark command actions." + :parent embark-function-map + "x" #'execute-extended-command + "I" #'Info-goto-emacs-command-node + "b" #'where-is + "g" #'global-set-key + "l" #'local-set-key) + +(defvar-keymap embark-package-map + :doc "Keymap for Embark package actions." + :parent embark-general-map + "RET" #'describe-package + "h" #'describe-package + "i" #'package-install + "I" #'embark-insert + "d" #'package-delete + "r" #'package-reinstall + "u" #'embark-browse-package-url + "W" #'embark-save-package-url + "a" #'package-autoremove + "g" #'package-refresh-contents + "m" #'elp-instrument-package ;; m=measure + "M" (if (fboundp 'embark-elp-restore-package) + 'embark-elp-restore-package + 'elp-restore-package)) + +(defvar-keymap embark-bookmark-map + :doc "Keymap for Embark bookmark actions." + :parent embark-general-map + "RET" #'bookmark-jump + "s" #'bookmark-set + "d" #'bookmark-delete + "r" #'bookmark-rename + "R" #'bookmark-relocate + "l" #'bookmark-locate + "<" #'bookmark-insert + "j" #'bookmark-jump + "o" #'bookmark-jump-other-window + "f" #'bookmark-jump-other-frame + "a" 'bookmark-show-annotation + "e" 'bookmark-edit-annotation + "x" #'embark-bookmark-open-externally + "$" #'eshell) + +(defvar-keymap embark-unicode-name-map + :doc "Keymap for Embark Unicode name actions." + :parent embark-general-map + "RET" #'insert-char + "I" #'insert-char + "W" #'embark-save-unicode-character) + +(defvar-keymap embark-prose-map + :doc "Keymap for Embark actions for dealing with prose." + :parent embark-general-map + "$" #'ispell-region + "f" #'fill-region + "u" #'upcase-region + "l" #'downcase-region + "c" #'capitalize-region + "F" #'whitespace-cleanup-region + "=" #'count-words-region) + +(defvar-keymap embark-sentence-map + :doc "Keymap for Embark actions for dealing with sentences." + :parent embark-prose-map + "t" #'transpose-sentences + "n" #'forward-sentence + "p" #'backward-sentence) + +(defvar-keymap embark-paragraph-map + :doc "Keymap for Embark actions for dealing with paragraphs." + :parent embark-prose-map + "t" #'transpose-paragraphs + "n" #'forward-paragraph + "p" #'backward-paragraph + "R" #'repunctuate-sentences) + +(defvar-keymap embark-flymake-map + :doc "Keymap for Embark actions on Flymake diagnostics." + :parent embark-general-map + "RET" 'flymake-show-buffer-diagnostics + "n" 'flymake-goto-next-error + "p" 'flymake-goto-prev-error) + +(defvar-keymap embark-become-help-map + :doc "Keymap for Embark help actions." + :parent embark-meta-map + "V" #'apropos-variable + "U" #'apropos-user-option + "C" #'apropos-command + "v" #'describe-variable + "f" #'describe-function + "s" #'describe-symbol + "F" #'describe-face + "p" #'describe-package + "i" #'describe-input-method) + +(autoload 'recentf-open-files "recentf" nil t) + +(defvar-keymap embark-become-file+buffer-map + :doc "Embark become keymap for files and buffers." + :parent embark-meta-map + "f" #'find-file + "4 f" #'find-file-other-window + "." #'find-file-at-point + "p" #'project-find-file + "r" #'recentf-open-files + "b" #'switch-to-buffer + "4 b" #'switch-to-buffer-other-window + "l" #'locate + "L" #'find-library + "v" #'vc-dir) + +(defvar-keymap embark-become-shell-command-map + :doc "Embark become keymap for shell commands." + :parent embark-meta-map + "!" #'shell-command + "&" #'async-shell-command + "c" #'comint-run + "t" #'term) + +(defvar-keymap embark-become-match-map + :doc "Embark become keymap for search." + :parent embark-meta-map + "o" #'occur + "k" #'keep-lines + "f" #'flush-lines + "c" #'count-matches) + +(provide 'embark) + +;; Check that embark-consult is installed. If Embark is used in +;; combination with Consult, you should install the integration package, +;; such that features like embark-export from consult-grep work as +;; expected. + +(with-eval-after-load 'consult + (unless (require 'embark-consult nil 'noerror) + (warn "The package embark-consult should be installed if you use both Embark and Consult"))) + +(with-eval-after-load 'org + (require 'embark-org)) + +;;; embark.el ends here diff --git a/emacs/elpa/embark-20240607.2213/embark.elc b/emacs/elpa/embark-20240607.2213/embark.elc Binary files differ. diff --git a/emacs/elpa/embark-20240419.452/embark.info b/emacs/elpa/embark-20240607.2213/embark.info diff --git a/emacs/elpa/magit-20240522.204/magit-diff.elc b/emacs/elpa/magit-20240522.204/magit-diff.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-pkg.el b/emacs/elpa/magit-20240522.204/magit-pkg.el @@ -1,20 +0,0 @@ -(define-package "magit" "20240522.204" "A Git porcelain inside Emacs." - '((emacs "26.1") - (compat "29.1.4.5") - (dash "20240405") - (git-commit "20240429") - (magit-section "20240429") - (seq "2.24") - (transient "20240421") - (with-editor "20240415")) - :commit "f9268a959828d0c6ab26171dd2fb1ffc55e5ae70" :authors - '(("Marius Vollmer" . "marius.vollmer@gmail.com") - ("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev")) - :maintainer - '("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") - :keywords - '("git" "tools" "vc") - :url "https://github.com/magit/magit") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/magit-20240522.204/magit-transient.elc b/emacs/elpa/magit-20240522.204/magit-transient.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit.info b/emacs/elpa/magit-20240522.204/magit.info @@ -1,11595 +0,0 @@ -This is magit.info, produced by makeinfo version 6.7 from magit.texi. - - Copyright (C) 2015-2024 Jonas Bernoulli - <emacs.magit@jonas.bernoulli.dev> - - You can redistribute this document and/or modify it under the terms - of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) - any later version. - - This document is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - -INFO-DIR-SECTION Emacs -START-INFO-DIR-ENTRY -* Magit: (magit). Using Git from Emacs with Magit. -END-INFO-DIR-ENTRY - - -File: magit.info, Node: Top, Next: Introduction, Up: (dir) - -Magit User Manual -***************** - -Magit is an interface to the version control system Git, implemented as -an Emacs package. Magit aspires to be a complete Git porcelain. While -we cannot (yet) claim that Magit wraps and improves upon each and every -Git command, it is complete enough to allow even experienced Git users -to perform almost all of their daily version control tasks directly from -within Emacs. While many fine Git clients exist, only Magit and Git -itself deserve to be called porcelains. - -This manual is for Magit version 3.3.0.50-git. - - Copyright (C) 2015-2024 Jonas Bernoulli - <emacs.magit@jonas.bernoulli.dev> - - You can redistribute this document and/or modify it under the terms - of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) - any later version. - - This document is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - -* Menu: - -* Introduction:: -* Installation:: -* Getting Started:: -* Interface Concepts:: -* Inspecting:: -* Manipulating:: -* Transferring:: -* Miscellaneous:: -* Customizing:: -* Plumbing:: -* FAQ:: -* Debugging Tools:: -* Keystroke Index:: -* Function and Command Index:: -* Variable Index:: - -— The Detailed Node Listing — - -Installation - -* Installing from Melpa:: -* Installing from the Git Repository:: -* Post-Installation Tasks:: - -Interface Concepts - -* Modes and Buffers:: -* Sections:: -* Transient Commands:: -* Transient Arguments and Buffer Variables:: -* Completion, Confirmation and the Selection: Completion Confirmation and the Selection. -* Mouse Support:: -* Running Git:: - -Modes and Buffers - -* Switching Buffers:: -* Naming Buffers:: -* Quitting Windows:: -* Automatic Refreshing of Magit Buffers:: -* Automatic Saving of File-Visiting Buffers:: -* Automatic Reverting of File-Visiting Buffers:: - - -Sections - -* Section Movement:: -* Section Visibility:: -* Section Hooks:: -* Section Types and Values:: -* Section Options:: - - -Completion, Confirmation and the Selection - -* Action Confirmation:: -* Completion and Confirmation:: -* The Selection:: -* The hunk-internal region:: -* Support for Completion Frameworks:: -* Additional Completion Options:: - - -Running Git - -* Viewing Git Output:: -* Git Process Status:: -* Running Git Manually:: -* Git Executable:: -* Global Git Arguments:: - - -Inspecting - -* Status Buffer:: -* Repository List:: -* Logging:: -* Diffing:: -* Ediffing:: -* References Buffer:: -* Bisecting:: -* Visiting Files and Blobs:: -* Blaming:: - -Status Buffer - -* Status Sections:: -* Status Header Sections:: -* Status Module Sections:: -* Status Options:: - - -Logging - -* Refreshing Logs:: -* Log Buffer:: -* Log Margin:: -* Select from Log:: -* Reflog:: -* Cherries:: - - -Diffing - -* Refreshing Diffs:: -* Commands Available in Diffs:: -* Diff Options:: -* Revision Buffer:: - - -References Buffer - -* References Sections:: - - -Visiting Files and Blobs - -* General-Purpose Visit Commands:: -* Visiting Files and Blobs from a Diff:: - - -Manipulating - -* Creating Repository:: -* Cloning Repository:: -* Staging and Unstaging:: -* Applying:: -* Committing:: -* Branching:: -* Merging:: -* Resolving Conflicts:: -* Rebasing:: -* Cherry Picking:: -* Resetting:: -* Stashing:: - -Staging and Unstaging - -* Staging from File-Visiting Buffers:: - - -Committing - -* Initiating a Commit:: -* Editing Commit Messages:: - - -Branching - -* The Two Remotes:: -* Branch Commands:: -* Branch Git Variables:: -* Auxiliary Branch Commands:: - - -Rebasing - -* Editing Rebase Sequences:: -* Information About In-Progress Rebase:: - - -Cherry Picking - -* Reverting:: - - -Transferring - -* Remotes:: -* Fetching:: -* Pulling:: -* Pushing:: -* Plain Patches:: -* Maildir Patches:: - -Remotes - -* Remote Commands:: -* Remote Git Variables:: - - -Miscellaneous - -* Tagging:: -* Notes:: -* Submodules:: -* Subtree:: -* Worktree:: -* Sparse checkouts:: -* Bundle:: -* Common Commands:: -* Wip Modes:: -* Commands for Buffers Visiting Files:: -* Minor Mode for Buffers Visiting Blobs:: - -Submodules - -* Listing Submodules:: -* Submodule Transient:: - - -Wip Modes - -* Wip Graph:: -* Legacy Wip Modes:: - - -Customizing - -* Per-Repository Configuration:: -* Essential Settings:: - -Essential Settings - -* Safety:: -* Performance:: -* Global Bindings:: - - -Plumbing - -* Calling Git:: -* Section Plumbing:: -* Refreshing Buffers:: -* Conventions:: - -Calling Git - -* Getting a Value from Git:: -* Calling Git for Effect:: - - -Section Plumbing - -* Creating Sections:: -* Section Selection:: -* Matching Sections:: - - -Conventions - -* Theming Faces:: - - -FAQ - -* FAQ - How to ...?:: -* FAQ - Issues and Errors:: - -FAQ - How to ...? - -* How to pronounce Magit?:: -* How to show git's output?:: -* How to install the gitman info manual?:: -* How to show diffs for gpg-encrypted files?:: -* How does branching and pushing work?:: -* Should I disable VC?:: - - -FAQ - Issues and Errors - -* Magit is slow:: -* I changed several thousand files at once and now Magit is unusable:: -* I am having problems committing:: -* I am using MS Windows and cannot push with Magit:: -* I am using macOS and SOMETHING works in shell, but not in Magit: I am using macOS and SOMETHING works in shell but not in Magit. -* Expanding a file to show the diff causes it to disappear:: -* Point is wrong in the COMMIT_EDITMSG buffer:: -* The mode-line information isn't always up-to-date:: -* A branch and tag sharing the same name breaks SOMETHING:: -* My Git hooks work on the command-line but not inside Magit:: -* git-commit-mode isn't used when committing from the command-line:: -* Point ends up inside invisible text when jumping to a file-visiting buffer:: -* I am no longer able to save popup defaults:: - - - - -File: magit.info, Node: Introduction, Next: Installation, Prev: Top, Up: Top - -1 Introduction -************** - -Magit is an interface to the version control system Git, implemented as -an Emacs package. Magit aspires to be a complete Git porcelain. While -we cannot (yet) claim that Magit wraps and improves upon each and every -Git command, it is complete enough to allow even experienced Git users -to perform almost all of their daily version control tasks directly from -within Emacs. While many fine Git clients exist, only Magit and Git -itself deserve to be called porcelains. - - Staging and otherwise applying changes is one of the most important -features in a Git porcelain and here Magit outshines anything else, -including Git itself. Git’s own staging interface (‘git add --patch’) -is so cumbersome that many users only use it in exceptional cases. In -Magit staging a hunk or even just part of a hunk is as trivial as -staging all changes made to a file. - - The most visible part of Magit’s interface is the status buffer, -which displays information about the current repository. Its content is -created by running several Git commands and making their output -actionable. Among other things, it displays information about the -current branch, lists unpulled and unpushed changes and contains -sections displaying the staged and unstaged changes. That might sound -noisy, but, since sections are collapsible, it’s not. - - To stage or unstage a change one places the cursor on the change and -then types ‘s’ or ‘u’. The change can be a file or a hunk, or when the -region is active (i.e., when there is a selection) several files or -hunks, or even just part of a hunk. The change or changes that these -commands - and many others - would act on are highlighted. - - Magit also implements several other "apply variants" in addition to -staging and unstaging. One can discard or reverse a change, or apply it -to the working tree. Git’s own porcelain only supports this for staging -and unstaging and you would have to do something like ‘git diff ... | -??? | git apply ...’ to discard, revert, or apply a single hunk on the -command line. In fact that’s exactly what Magit does internally (which -is what lead to the term "apply variants"). - - Magit isn’t just for Git experts, but it does assume some prior -experience with Git as well as Emacs. That being said, many users have -reported that using Magit was what finally taught them what Git is -capable of and how to use it to its fullest. Other users wished they -had switched to Emacs sooner so that they would have gotten their hands -on Magit earlier. - - While one has to know the basic features of Emacs to be able to make -full use of Magit, acquiring just enough Emacs skills doesn’t take long -and is worth it, even for users who prefer other editors. Vim users are -advised to give Evil (https://github.com/emacs-evil/evil), the -"Extensible VI Layer for Emacs", and Spacemacs -(https://github.com/syl20bnr/spacemacs), an "Emacs starter-kit focused -on Evil" a try. - - Magit provides a consistent and efficient Git porcelain. After a -short learning period, you will be able to perform most of your daily -version control tasks faster than you would on the command line. You -will likely also start using features that seemed too daunting in the -past. - - Magit fully embraces Git. It exposes many advanced features using a -simple but flexible interface instead of only wrapping the trivial ones -like many GUI clients do. Of course Magit supports logging, cloning, -pushing, and other commands that usually don’t fail in spectacular ways; -but it also supports tasks that often cannot be completed in a single -step. Magit fully supports tasks such as merging, rebasing, -cherry-picking, reverting, and blaming by not only providing a command -to initiate these tasks but also by displaying context sensitive -information along the way and providing commands that are useful for -resolving conflicts and resuming the sequence after doing so. - - Magit wraps and in many cases improves upon at least the following -Git porcelain commands: ‘add’, ‘am’, ‘bisect’, ‘blame’, ‘branch’, -‘checkout’, ‘cherry’, ‘cherry-pick’, ‘clean’, ‘clone’, ‘commit’, -‘config’, ‘describe’, ‘diff’, ‘fetch’, ‘format-patch’, ‘init’, ‘log’, -‘merge’, ‘merge-tree’, ‘mv’, ‘notes’, ‘pull’, ‘rebase’, ‘reflog’, -‘remote’, ‘request-pull’, ‘reset’, ‘revert’, ‘rm’, ‘show’, ‘stash’, -‘submodule’, ‘subtree’, ‘tag’, and ‘worktree.’ Many more Magit porcelain -commands are implemented on top of Git plumbing commands. - - -File: magit.info, Node: Installation, Next: Getting Started, Prev: Introduction, Up: Top - -2 Installation -************** - -Magit can be installed using Emacs’ package manager or manually from its -development repository. - -* Menu: - -* Installing from Melpa:: -* Installing from the Git Repository:: -* Post-Installation Tasks:: - - -File: magit.info, Node: Installing from Melpa, Next: Installing from the Git Repository, Up: Installation - -2.1 Installing from Melpa -========================= - -Magit is available from Melpa and Melpa-Stable. If you haven’t used -Emacs’ package manager before, then it is high time you familiarize -yourself with it by reading the documentation in the Emacs manual, see -*note (emacs)Packages::. Then add one of the archives to -‘package-archives’: - - • To use Melpa: - - (require 'package) - (add-to-list 'package-archives - '("melpa" . "https://melpa.org/packages/") t) - - • To use Melpa-Stable: - - (require 'package) - (add-to-list 'package-archives - '("melpa-stable" . "https://stable.melpa.org/packages/") t) - - Once you have added your preferred archive, you need to update the -local package list using: - - M-x package-refresh-contents RET - - Once you have done that, you can install Magit and its dependencies -using: - - M-x package-install RET magit RET - - Now see *note Post-Installation Tasks::. - - -File: magit.info, Node: Installing from the Git Repository, Next: Post-Installation Tasks, Prev: Installing from Melpa, Up: Installation - -2.2 Installing from the Git Repository -====================================== - -Magit depends on the ‘compat’, ‘dash’, ‘transient’ and ‘with-editor’ -libraries which are available from Melpa and Melpa-Stable. Install them -using ‘M-x package-install RET <package> RET’. Of course you may also -install them manually from their repository. - - Then clone the Magit repository: - - $ git clone https://github.com/magit/magit.git ~/.emacs.d/site-lisp/magit - $ cd ~/.emacs.d/site-lisp/magit - - Then compile the libraries and generate the info manuals: - - $ make - - If you haven’t installed ‘compat’, ‘dash’, ‘transient’ and -‘with-editor’ from Melpa or at ‘/path/to/magit/../<package>’, then you -have to tell ‘make’ where to find them. To do so create the file -‘/path/to/magit/config.mk’ with the following content before running -‘make’: - - LOAD_PATH = -L ~/.emacs.d/site-lisp/magit/lisp - LOAD_PATH += -L ~/.emacs.d/site-lisp/dash - LOAD_PATH += -L ~/.emacs.d/site-lisp/transient/lisp - LOAD_PATH += -L ~/.emacs.d/site-lisp/with-editor/lisp - LOAD_PATH += -L ~/.emacs.d/site-lisp/compat - - Finally add this to your init file: - - (add-to-list 'load-path "~/.emacs.d/site-lisp/magit/lisp") - (require 'magit) - - (with-eval-after-load 'info - (info-initialize) - (add-to-list 'Info-directory-list - "~/.emacs.d/site-lisp/magit/Documentation/")) - - Of course if you installed the dependencies manually as well, then -you have to tell Emacs about them too, by prefixing the above with: - - (add-to-list 'load-path "~/.emacs.d/site-lisp/dash") - (add-to-list 'load-path "~/.emacs.d/site-lisp/transient/lisp") - (add-to-list 'load-path "~/.emacs.d/site-lisp/with-editor") - - Note that you have to add the ‘lisp’ subdirectory to the ‘load-path’, -not the top-level of the repository, and that elements of ‘load-path’ -should not end with a slash, while those of ‘Info-directory-list’ -should. - - Instead of requiring the feature ‘magit’, you could load just the -autoload definitions, by loading the file ‘magit-autoloads.el’. - - (load "/path/to/magit/lisp/magit-autoloads") - - Instead of running Magit directly from the repository by adding that -to the ‘load-path’, you might want to instead install it in some other -directory using ‘sudo make install’ and setting ‘load-path’ accordingly. - - To update Magit use: - - $ git pull - $ make - - At times it might be necessary to run ‘make clean all’ instead. - - To view all available targets use ‘make help’. - - Now see *note Post-Installation Tasks::. - - -File: magit.info, Node: Post-Installation Tasks, Prev: Installing from the Git Repository, Up: Installation - -2.3 Post-Installation Tasks -=========================== - -After installing Magit you should verify that you are indeed using the -Magit, Git, and Emacs releases you think you are using. It’s best to -restart Emacs before doing so, to make sure you are not using an -outdated value for ‘load-path’. - - M-x magit-version RET - - should display something like - - Magit 2.8.0, Git 2.10.2, Emacs 25.1.1, gnu/linux - - Then you might also want to read about options that many users likely -want to customize. See *note Essential Settings::. - - To be able to follow cross references to Git manpages found in this -manual, you might also have to manually install the ‘gitman’ info -manual, or advice ‘Info-follow-nearest-node’ to instead open the actual -manpage. See *note How to install the gitman info manual?::. - - If you are completely new to Magit then see *note Getting Started::. - - If you run into problems, then please see the *note FAQ::. Also see -the *note Debugging Tools::. - - And last but not least please consider making a donation, to ensure -that I can keep working on Magit. See <https://magit.vc/donations>. -for various donation options. - - -File: magit.info, Node: Getting Started, Next: Interface Concepts, Prev: Installation, Up: Top - -3 Getting Started -***************** - -This short tutorial describes the most essential features that many -Magitians use on a daily basis. It only scratches the surface but -should be enough to get you started. - - IMPORTANT: It is safest if you clone some repository just for this -tutorial. Alternatively you can use an existing local repository, but -if you do that, then you should commit all uncommitted changes before -proceeding. - - Type ‘C-x g’ to display information about the current Git repository -in a dedicated buffer, called the status buffer. - - Most Magit commands are commonly invoked from the status buffer. It -can be considered the primary interface for interacting with Git using -Magit. Many other Magit buffers may exist at a given time, but they are -often created from this buffer. - - Depending on what state your repository is in, this buffer may -contain sections titled "Staged changes", "Unstaged changes", "Unmerged -into origin/master", "Unpushed to origin/master", and many others. - - Since we are starting from a safe state, which you can easily return -to (by doing a ‘git reset --hard PRE-MAGIT-STATE’), there currently are -no staged or unstaged changes. Edit some files and save the changes. -Then go back to the status buffer, while at the same time refreshing it, -by typing ‘C-x g’. (When the status buffer, or any Magit buffer for -that matter, is the current buffer, then you can also use just ‘g’ to -refresh it). - - Move between sections using ‘p’ and ‘n’. Note that the bodies of -some sections are hidden. Type ‘TAB’ to expand or collapse the section -at point. You can also use ‘C-tab’ to cycle the visibility of the -current section and its children. Move to a file section inside the -section named "Unstaged changes" and type ‘s’ to stage the changes you -have made to that file. That file now appears under "Staged changes". - - Magit can stage and unstage individual hunks, not just complete -files. Move to the file you have just staged, expand it using ‘TAB’, -move to one of the hunks using ‘n’, and unstage just that by typing ‘u’. -Note how the staging (‘s’) and unstaging (‘u’) commands operate on the -change at point. Many other commands behave the same way. - - You can also un-/stage just part of a hunk. Inside the body of a -hunk section (move there using ‘C-n’), set the mark using ‘C-SPC’ and -move down until some added and/or removed lines fall inside the region -but not all of them. Again type ‘s’ to stage. - - It is also possible to un-/stage multiple files at once. Move to a -file section, type ‘C-SPC’, move to the next file using ‘n’, and then -‘s’ to stage both files. Note that both the mark and point have to be -on the headings of sibling sections for this to work. If the region -looks like it does in other buffers, then it doesn’t select Magit -sections that can be acted on as a unit. - - And then of course you want to commit your changes. Type ‘c’. This -shows the available commit commands and arguments in a buffer at the -bottom of the frame. Each command and argument is prefixed with the key -that invokes/sets it. Do not worry about this for now. We want to -create a "normal" commit, which is done by typing ‘c’ again. - - Now two new buffers appear. One is for writing the commit message, -the other shows a diff with the changes that you are about to commit. -Write a message and then type ‘C-c C-c’ to actually create the commit. - - You probably don’t want to push the commit you just created because -you just committed some random changes, but if that is not the case you -could push it by typing ‘P’ to show all the available push commands and -arguments and then ‘p’ to push to a branch with the same name as the -local branch onto the remote configured as the push-remote. (If the -push-remote is not configured yet, then you would first be prompted for -the remote to push to.) - - So far we have mentioned the commit and push menu commands. These -are probably among the menus you will be using the most, but many others -exist. To show a menu that lists all other menus (as well as the -various apply commands and some other essential commands), type ‘h’. -Try a few. (Such menus are also called "transient prefix commands" or -just "transients".) - - The key bindings in that menu correspond to the bindings in Magit -buffers, including but not limited to the status buffer. So you could -type ‘h d’ to bring up the diff menu, but once you remember that "d" -stands for "diff", you would usually do so by just typing ‘d’. - - This "prefix of prefixes" is useful even once you have memorized all -the bindings, as it can provide easy access to Magit commands from -non-Magit buffers. So, by default, it is globally bound to ‘C-x M-g’. - - A similar menu featuring (for the most part) commands that act on -just the file being visited in the current buffer, is globally bound to -‘C-c M-g’. That binding can also be used in buffers, which do not visit -a file, but then only a subset of the commands is available. - - The global key bindings mentioned in the previous two paragraphs are -quite inconvenient. We recommend using ‘C-c g’ and ‘C-c f’ instead, but -cannot use those key sequences by default because they are strictly -reserved for bindings added by the user. See *note Global Bindings::, -if you want to explicitly opt-in to the recommended key bindings. - - Magit also provides context menus and other mouse commands, see *note -Mouse Support::. - - It is not necessary that you do so now, but if you stick with Magit, -then it is highly recommended that you read the next section too. - - -File: magit.info, Node: Interface Concepts, Next: Inspecting, Prev: Getting Started, Up: Top - -4 Interface Concepts -******************** - -* Menu: - -* Modes and Buffers:: -* Sections:: -* Transient Commands:: -* Transient Arguments and Buffer Variables:: -* Completion, Confirmation and the Selection: Completion Confirmation and the Selection. -* Mouse Support:: -* Running Git:: - - -File: magit.info, Node: Modes and Buffers, Next: Sections, Up: Interface Concepts - -4.1 Modes and Buffers -===================== - -Magit provides several major-modes. For each of these modes there -usually exists only one buffer per repository. Separate modes and thus -buffers exist for commits, diffs, logs, and some other things. - - Besides these special purpose buffers, there also exists an overview -buffer, called the *status buffer*. It’s usually from this buffer that -the user invokes Git commands, or creates or visits other buffers. - - In this manual we often speak about "Magit buffers". By that we mean -buffers whose major-modes derive from ‘magit-mode’. - -‘M-x magit-toggle-buffer-lock’ - This command locks the current buffer to its value or if the buffer - is already locked, then it unlocks it. - - Locking a buffer to its value prevents it from being reused to - display another value. The name of a locked buffer contains its - value, which allows telling it apart from other locked buffers and - the unlocked buffer. - - Not all Magit buffers can be locked to their values; for example, - it wouldn’t make sense to lock a status buffer. - - There can only be a single unlocked buffer using a certain - major-mode per repository. So when a buffer is being unlocked and - another unlocked buffer already exists for that mode and - repository, then the former buffer is instead deleted and the - latter is displayed in its place. - -* Menu: - -* Switching Buffers:: -* Naming Buffers:: -* Quitting Windows:: -* Automatic Refreshing of Magit Buffers:: -* Automatic Saving of File-Visiting Buffers:: -* Automatic Reverting of File-Visiting Buffers:: - - -File: magit.info, Node: Switching Buffers, Next: Naming Buffers, Up: Modes and Buffers - -4.1.1 Switching Buffers ------------------------ - - -- Function: magit-display-buffer buffer &optional display-function - This function is a wrapper around ‘display-buffer’ and is used to - display any Magit buffer. It displays BUFFER in some window and, - unlike ‘display-buffer’, also selects that window, provided - ‘magit-display-buffer-noselect’ is ‘nil’. It also runs the hooks - mentioned below. - - If optional DISPLAY-FUNCTION is non-nil, then that is used to - display the buffer. Usually that is ‘nil’ and the function - specified by ‘magit-display-buffer-function’ is used. - - -- Variable: magit-display-buffer-noselect - When this is non-nil, then ‘magit-display-buffer’ only displays the - buffer but forgoes also selecting the window. This variable should - not be set globally, it is only intended to be let-bound, by code - that automatically updates "the other window". This is used for - example when the revision buffer is updated when you move inside - the log buffer. - - -- User Option: magit-display-buffer-function - The function specified here is called by ‘magit-display-buffer’ - with one argument, a buffer, to actually display that buffer. This - function should call ‘display-buffer’ with that buffer as first and - a list of display actions as second argument. - - Magit provides several functions, listed below, that are suitable - values for this option. If you want to use different rules, then a - good way of doing that is to start with a copy of one of these - functions and then adjust it to your needs. - - Instead of using a wrapper around ‘display-buffer’, that function - itself can be used here, in which case the display actions have to - be specified by adding them to ‘display-buffer-alist’ instead. - - To learn about display actions, see *note (elisp)Choosing Window::. - - -- Function: magit-display-buffer-traditional buffer - This function is the current default value of the option - ‘magit-display-buffer-function’. Before that option and this - function were added, the behavior was hard-coded in many places all - over the code base but now all the rules are contained in this one - function (except for the "noselect" special case mentioned above). - - -- Function: magit-display-buffer-same-window-except-diff-v1 - This function displays most buffers in the currently selected - window. If a buffer’s mode derives from ‘magit-diff-mode’ or - ‘magit-process-mode’, it is displayed in another window. - - -- Function: magit-display-buffer-fullframe-status-v1 - This function fills the entire frame when displaying a status - buffer. Otherwise, it behaves like - ‘magit-display-buffer-traditional’. - - -- Function: magit-display-buffer-fullframe-status-topleft-v1 - This function fills the entire frame when displaying a status - buffer. It behaves like ‘magit-display-buffer-fullframe-status-v1’ - except that it displays buffers that derive from ‘magit-diff-mode’ - or ‘magit-process-mode’ to the top or left of the current buffer - rather than to the bottom or right. As a result, Magit buffers - tend to pop up on the same side as they would if - ‘magit-display-buffer-traditional’ were in use. - - -- Function: magit-display-buffer-fullcolumn-most-v1 - This function displays most buffers so that they fill the entire - height of the frame. However, the buffer is displayed in another - window if (1) the buffer’s mode derives from ‘magit-process-mode’, - or (2) the buffer’s mode derives from ‘magit-diff-mode’, provided - that the mode of the current buffer derives from ‘magit-log-mode’ - or ‘magit-cherry-mode’. - - -- User Option: magit-pre-display-buffer-hook - This hook is run by ‘magit-display-buffer’ before displaying the - buffer. - - -- Function: magit-save-window-configuration - This function saves the current window configuration. Later when - the buffer is buried, it may be restored by - ‘magit-restore-window-configuration’. - - -- User Option: magit-post-display-buffer-hook - This hook is run by ‘magit-display-buffer’ after displaying the - buffer. - - -- Function: magit-maybe-set-dedicated - This function remembers if a new window had to be created to - display the buffer, or whether an existing window was reused. This - information is later used by ‘magit-mode-quit-window’, to determine - whether the window should be deleted when its last Magit buffer is - buried. - - -File: magit.info, Node: Naming Buffers, Next: Quitting Windows, Prev: Switching Buffers, Up: Modes and Buffers - -4.1.2 Naming Buffers --------------------- - - -- User Option: magit-generate-buffer-name-function - The function used to generate the names of Magit buffers. - - Such a function should take the options - ‘magit-uniquify-buffer-names’ as well as ‘magit-buffer-name-format’ - into account. If it doesn’t, then should be clearly stated in the - doc-string. And if it supports %-sequences beyond those mentioned - in the doc-string of the option ‘magit-buffer-name-format’, then - its own doc-string should describe the additions. - - -- Function: magit-generate-buffer-name-default-function mode - This function returns a buffer name suitable for a buffer whose - major-mode is MODE and which shows information about the repository - in which ‘default-directory’ is located. - - This function uses ‘magit-buffer-name-format’ and supporting all of - the %-sequences mentioned the documentation of that option. It - also respects the option ‘magit-uniquify-buffer-names’. - - -- User Option: magit-buffer-name-format - The format string used to name Magit buffers. - - At least the following %-sequences are supported: - - • ‘%m’ - - The name of the major-mode, but with the ‘-mode’ suffix - removed. - - • ‘%M’ - - Like ‘%m’ but abbreviate ‘magit-status-mode’ as ‘magit’. - - • ‘%v’ - - The value the buffer is locked to, in parentheses, or an empty - string if the buffer is not locked to a value. - - • ‘%V’ - - Like ‘%v’, but the string is prefixed with a space, unless it - is an empty string. - - • ‘%t’ - - The top-level directory of the working tree of the repository, - or if ‘magit-uniquify-buffer-names’ is non-nil an abbreviation - of that. - - • ‘%x’ - - If ‘magit-uniquify-buffer-names’ is nil "*", otherwise the - empty string. Due to limitations of the ‘uniquify’ package, - buffer names must end with the path. - - The value should always contain ‘%m’ or ‘%M’, ‘%v’ or ‘%V’, and - ‘%t’. If ‘magit-uniquify-buffer-names’ is non-nil, then the value - must end with ‘%t’ or ‘%t%x’. See issue #2841. - - -- User Option: magit-uniquify-buffer-names - This option controls whether the names of Magit buffers are - uniquified. If the names are not being uniquified, then they - contain the full path of the top-level of the working tree of the - corresponding repository. If they are being uniquified, then they - end with the basename of the top-level, or if that would conflict - with the name used for other buffers, then the names of all these - buffers are adjusted until they no longer conflict. - - This is done using the ‘uniquify’ package; customize its options to - control how buffer names are uniquified. - - -File: magit.info, Node: Quitting Windows, Next: Automatic Refreshing of Magit Buffers, Prev: Naming Buffers, Up: Modes and Buffers - -4.1.3 Quitting Windows ----------------------- - -‘q’ (‘magit-mode-bury-buffer’) - This command buries or kills the current Magit buffer. The - function specified by option ‘magit-bury-buffer-function’ is used - to bury the buffer when called without a prefix argument or to kill - it when called with a single prefix argument. - - When called with two or more prefix arguments then it always kills - all Magit buffers, associated with the current project, including - the current buffer. - - -- User Option: magit-bury-buffer-function - The function used to actually bury or kill the current buffer. - - ‘magit-mode-bury-buffer’ calls this function with one argument. If - the argument is non-nil, then the function has to kill the current - buffer. Otherwise it has to bury it alive. The default value - currently is ‘magit-mode-quit-window’. - - -- Function: magit-restore-window-configuration kill-buffer - Bury or kill the current buffer using ‘quit-window’, which is - called with KILL-BUFFER as first and the selected window as second - argument. - - Then restore the window configuration that existed right before the - current buffer was displayed in the selected frame. Unfortunately - that also means that point gets adjusted in all the buffers, which - are being displayed in the selected frame. - - -- Function: magit-mode-quit-window kill-buffer - Bury or kill the current buffer using ‘quit-window’, which is - called with KILL-BUFFER as first and the selected window as second - argument. - - Then, if the window was originally created to display a Magit - buffer and the buried buffer was the last remaining Magit buffer - that was ever displayed in the window, then that is deleted. - - -File: magit.info, Node: Automatic Refreshing of Magit Buffers, Next: Automatic Saving of File-Visiting Buffers, Prev: Quitting Windows, Up: Modes and Buffers - -4.1.4 Automatic Refreshing of Magit Buffers -------------------------------------------- - -After running a command which may change the state of the current -repository, the current Magit buffer and the corresponding status buffer -are refreshed. The status buffer can be automatically refreshed -whenever a buffer is saved to a file inside the respective repository by -adding a hook, like so: - - (with-eval-after-load 'magit-mode - (add-hook 'after-save-hook 'magit-after-save-refresh-status t)) - - Automatically refreshing Magit buffers ensures that the displayed -information is up-to-date most of the time but can lead to a noticeable -delay in big repositories. Other Magit buffers are not refreshed to -keep the delay to a minimum and also because doing so can sometimes be -undesirable. - - Buffers can also be refreshed explicitly, which is useful in buffers -that weren’t current during the last refresh and after changes were made -to the repository outside of Magit. - -‘g’ (‘magit-refresh’) - This command refreshes the current buffer if its major mode derives - from ‘magit-mode’ as well as the corresponding status buffer. - - If the option ‘magit-revert-buffers’ calls for it, then it also - reverts all unmodified buffers that visit files being tracked in - the current repository. - -‘G’ (‘magit-refresh-all’) - This command refreshes all Magit buffers belonging to the current - repository and also reverts all unmodified buffers that visit files - being tracked in the current repository. - - The file-visiting buffers are always reverted, even if - ‘magit-revert-buffers’ is nil. - - -- User Option: magit-refresh-buffer-hook - This hook is run in each Magit buffer that was refreshed during the - current refresh - normally the current buffer and the status - buffer. - - -- User Option: magit-refresh-status-buffer - When this option is non-nil, then the status buffer is - automatically refreshed after running git for side-effects, in - addition to the current Magit buffer, which is always refreshed - automatically. - - Only set this to nil after exhausting all other options to improve - performance. - - -- Function: magit-after-save-refresh-status - This function is intended to be added to ‘after-save-hook’. After - doing that the corresponding status buffer is refreshed whenever a - buffer is saved to a file inside a repository. - - Note that refreshing a Magit buffer is done by re-creating its - contents from scratch, which can be slow in large repositories. If - you are not satisfied with Magit’s performance, then you should - obviously not add this function to that hook. - - -File: magit.info, Node: Automatic Saving of File-Visiting Buffers, Next: Automatic Reverting of File-Visiting Buffers, Prev: Automatic Refreshing of Magit Buffers, Up: Modes and Buffers - -4.1.5 Automatic Saving of File-Visiting Buffers ------------------------------------------------ - -File-visiting buffers are by default saved at certain points in time. -This doesn’t guarantee that Magit buffers are always up-to-date, but, -provided one only edits files by editing them in Emacs and uses only -Magit to interact with Git, one can be fairly confident. When in doubt -or after outside changes, type ‘g’ (‘magit-refresh’) to save and refresh -explicitly. - - -- User Option: magit-save-repository-buffers - This option controls whether file-visiting buffers are saved before - certain events. - - If this is non-nil then all modified file-visiting buffers - belonging to the current repository may be saved before running - commands, before creating new Magit buffers, and before explicitly - refreshing such buffers. If this is ‘dontask’ then this is done - without user intervention. If it is ‘t’ then the user has to - confirm each save. - - -File: magit.info, Node: Automatic Reverting of File-Visiting Buffers, Prev: Automatic Saving of File-Visiting Buffers, Up: Modes and Buffers - -4.1.6 Automatic Reverting of File-Visiting Buffers --------------------------------------------------- - -By default Magit automatically reverts buffers that are visiting files -that are being tracked in a Git repository, after they have changed on -disk. When using Magit one often changes files on disk by running Git, -i.e., "outside Emacs", making this a rather important feature. - - For example, if you discard a change in the status buffer, then that -is done by running ‘git apply --reverse ...’, and Emacs considers the -file to have "changed on disk". If Magit did not automatically revert -the buffer, then you would have to type ‘M-x revert-buffer RET RET’ in -the visiting buffer before you could continue making changes. - - -- User Option: magit-auto-revert-mode - When this mode is enabled, then buffers that visit tracked files - are automatically reverted after the visited files change on disk. - - -- User Option: global-auto-revert-mode - When this mode is enabled, then any file-visiting buffer is - automatically reverted after the visited file changes on disk. - - If you like buffers that visit tracked files to be automatically - reverted, then you might also like any buffer to be reverted, not - just those visiting tracked files. If that is the case, then - enable this mode _instead of_ ‘magit-auto-revert-mode’. - - -- User Option: magit-auto-revert-immediately - This option controls whether Magit reverts buffers immediately. - - If this is non-nil and either ‘global-auto-revert-mode’ or - ‘magit-auto-revert-mode’ is enabled, then Magit immediately reverts - buffers by explicitly calling ‘auto-revert-buffers’ after running - Git for side-effects. - - If ‘auto-revert-use-notify’ is non-nil (and file notifications are - actually supported), then ‘magit-auto-revert-immediately’ does not - have to be non-nil, because the reverts happen immediately anyway. - - If ‘magit-auto-revert-immediately’ and ‘auto-revert-use-notify’ are - both ‘nil’, then reverts happen after ‘auto-revert-interval’ - seconds of user inactivity. That is not desirable. - - -- User Option: auto-revert-use-notify - This option controls whether file notification functions should be - used. Note that this variable unfortunately defaults to ‘t’ even - on systems on which file notifications cannot be used. - - -- User Option: magit-auto-revert-tracked-only - This option controls whether ‘magit-auto-revert-mode’ only reverts - tracked files or all files that are located inside Git - repositories, including untracked files and files located inside - Git’s control directory. - - -- User Option: auto-revert-mode - The global mode ‘magit-auto-revert-mode’ works by turning on this - local mode in the appropriate buffers (but - ‘global-auto-revert-mode’ is implemented differently). You can - also turn it on or off manually, which might be necessary if Magit - does not notice that a previously untracked file now is being - tracked or vice-versa. - - -- User Option: auto-revert-stop-on-user-input - This option controls whether the arrival of user input suspends the - automatic reverts for ‘auto-revert-interval’ seconds. - - -- User Option: auto-revert-interval - This option controls how many seconds Emacs waits for before - resuming suspended reverts. - - -- User Option: auto-revert-buffer-list-filter - This option specifies an additional filter used by - ‘auto-revert-buffers’ to determine whether a buffer should be - reverted or not. - - This option is provided by Magit, which also advises - ‘auto-revert-buffers’ to respect it. Magit users who do not turn - on the local mode ‘auto-revert-mode’ themselves, are best served by - setting the value to ‘magit-auto-revert-repository-buffer-p’. - - However the default is nil, so as not to disturb users who do use - the local mode directly. If you experience delays when running - Magit commands, then you should consider using one of the - predicates provided by Magit - especially if you also use Tramp. - - Users who do turn on ‘auto-revert-mode’ in buffers in which Magit - doesn’t do that for them, should likely not use any filter. Users - who turn on ‘global-auto-revert-mode’, do not have to worry about - this option, because it is disregarded if the global mode is - enabled. - - -- User Option: auto-revert-verbose - This option controls whether Emacs reports when a buffer has been - reverted. - - The options with the ‘auto-revert-’ prefix are located in the Custom -group named ‘auto-revert’. The other, Magit-specific, options are -located in the ‘magit’ group. - -* Menu: - -* Risk of Reverting Automatically:: - - -File: magit.info, Node: Risk of Reverting Automatically, Up: Automatic Reverting of File-Visiting Buffers - -Risk of Reverting Automatically -............................... - -For the vast majority of users, automatically reverting file-visiting -buffers after they have changed on disk is harmless. - - If a buffer is modified (i.e., it contains changes that haven’t been -saved yet), then Emacs will refuse to automatically revert it. If you -save a previously modified buffer, then that results in what is seen by -Git as an uncommitted change. Git will then refuse to carry out any -commands that would cause these changes to be lost. In other words, if -there is anything that could be lost, then either Git or Emacs will -refuse to discard the changes. - - However, if you use file-visiting buffers as a sort of ad hoc -"staging area", then the automatic reverts could potentially cause data -loss. So far I have heard from only one user who uses such a workflow. - - An example: You visit some file in a buffer, edit it, and save the -changes. Then, outside of Emacs (or at least not using Magit or by -saving the buffer) you change the file on disk again. At this point the -buffer is the only place where the intermediate version still exists. -You have saved the changes to disk, but that has since been overwritten. -Meanwhile Emacs considers the buffer to be unmodified (because you have -not made any changes to it since you last saved it to the visited file) -and therefore would not object to it being automatically reverted. At -this point an Auto-Revert mode would kick in. It would check whether -the buffer is modified and since that is not the case it would revert -it. The intermediate version would be lost. (Actually you could still -get it back using the ‘undo’ command.) - - If your workflow depends on Emacs preserving the intermediate version -in the buffer, then you have to disable all Auto-Revert modes. But -please consider that such a workflow would be dangerous even without -using an Auto-Revert mode, and should therefore be avoided. If Emacs -crashes or if you quit Emacs by mistake, then you would also lose the -buffer content. There would be no autosave file still containing the -intermediate version (because that was deleted when you saved the -buffer) and you would not be asked whether you want to save the buffer -(because it isn’t modified). - - -File: magit.info, Node: Sections, Next: Transient Commands, Prev: Modes and Buffers, Up: Interface Concepts - -4.2 Sections -============ - -Magit buffers are organized into nested sections, which can be collapsed -and expanded, similar to how sections are handled in Org mode. Each -section also has a type, and some sections also have a value. For each -section type there can also be a local keymap, shared by all sections of -that type. - - Taking advantage of the section value and type, many commands operate -on the current section, or when the region is active and selects -sections of the same type, all of the selected sections. Commands that -only make sense for a particular section type (as opposed to just -behaving differently depending on the type) are usually bound in section -type keymaps. - -* Menu: - -* Section Movement:: -* Section Visibility:: -* Section Hooks:: -* Section Types and Values:: -* Section Options:: - - -File: magit.info, Node: Section Movement, Next: Section Visibility, Up: Sections - -4.2.1 Section Movement ----------------------- - -To move within a section use the usual keys (‘C-p’, ‘C-n’, ‘C-b’, ‘C-f’ -etc), whose global bindings are not shadowed. To move to another -section use the following commands. - -‘p’ (‘magit-section-backward’) - When not at the beginning of a section, then move to the beginning - of the current section. At the beginning of a section, instead - move to the beginning of the previous visible section. - -‘n’ (‘magit-section-forward’) - Move to the beginning of the next visible section. - -‘M-p’ (‘magit-section-backward-siblings’) - Move to the beginning of the previous sibling section. If there is - no previous sibling section, then move to the parent section - instead. - -‘M-n’ (‘magit-section-forward-siblings’) - Move to the beginning of the next sibling section. If there is no - next sibling section, then move to the parent section instead. - -‘^’ (‘magit-section-up’) - Move to the beginning of the parent of the current section. - - The above commands all call the hook ‘magit-section-movement-hook’. -Any of the functions listed below can be used as members of this hook. - - You might want to remove some of the functions that Magit adds using -‘add-hook’. In doing so you have to make sure you do not attempt to -remove function that haven’t even been added yet, for example: - - (with-eval-after-load 'magit-diff - (remove-hook 'magit-section-movement-hook - 'magit-hunk-set-window-start)) - - -- Variable: magit-section-movement-hook - This hook is run by all of the above movement commands, after - arriving at the destination. - - -- Function: magit-hunk-set-window-start - This hook function ensures that the beginning of the current - section is visible, provided it is a ‘hunk’ section. Otherwise, it - does nothing. - - Loading ‘magit-diff’ adds this function to the hook. - - -- Function: magit-section-set-window-start - This hook function ensures that the beginning of the current - section is visible, regardless of the section’s type. If you add - this to ‘magit-section-movement-hook’, then you must remove the - hunk-only variant in turn. - - -- Function: magit-log-maybe-show-more-commits - This hook function only has an effect in log buffers, and ‘point’ - is on the "show more" section. If that is the case, then it - doubles the number of commits that are being shown. - - Loading ‘magit-log’ adds this function to the hook. - - -- Function: magit-log-maybe-update-revision-buffer - When moving inside a log buffer, then this function updates the - revision buffer, provided it is already being displayed in another - window of the same frame. - - Loading ‘magit-log’ adds this function to the hook. - - -- Function: magit-log-maybe-update-blob-buffer - When moving inside a log buffer and another window of the same - frame displays a blob buffer, then this function instead displays - the blob buffer for the commit at point in that window. - - -- Function: magit-status-maybe-update-revision-buffer - When moving inside a status buffer, then this function updates the - revision buffer, provided it is already being displayed in another - window of the same frame. - - -- Function: magit-status-maybe-update-stash-buffer - When moving inside a status buffer, then this function updates the - stash buffer, provided it is already being displayed in another - window of the same frame. - - -- Function: magit-status-maybe-update-blob-buffer - When moving inside a status buffer and another window of the same - frame displays a blob buffer, then this function instead displays - the blob buffer for the commit at point in that window. - - -- Function: magit-stashes-maybe-update-stash-buffer - When moving inside a buffer listing stashes, then this function - updates the stash buffer, provided it is already being displayed in - another window of the same frame. - - -- User Option: magit-update-other-window-delay - Delay before automatically updating the other window. - - When moving around in certain buffers, then certain other buffers, - which are being displayed in another window, may optionally be - updated to display information about the section at point. - - When holding down a key to move by more than just one section, then - that would update that buffer for each section on the way. To - prevent that, updating the revision buffer is delayed, and this - option controls for how long. For optimal experience you might - have to adjust this delay and/or the keyboard repeat rate and delay - of your graphical environment or operating system. - - -File: magit.info, Node: Section Visibility, Next: Section Hooks, Prev: Section Movement, Up: Sections - -4.2.2 Section Visibility ------------------------- - -Magit provides many commands for changing the visibility of sections, -but all you need to get started are the next two. - -‘<TAB>’ (‘magit-section-toggle’) - Toggle the visibility of the body of the current section. - -‘C-c <TAB>’ (‘magit-section-cycle’) -‘C-<tab>’ (‘magit-section-cycle’) - Cycle the visibility of current section and its children. - - If this command is invoked using ‘C-<tab>’ and that is globally - bound to ‘tab-next’, then this command pivots to behave like that - command, and you must instead use ‘C-c TAB’ to cycle section - visibility. - - If you would like to keep using ‘C-<tab>’ to cycle section - visibility but also want to use ‘tab-bar-mode’, then you have to - prevent that mode from using this key and instead bind another key - to ‘tab-next’. Because ‘tab-bar-mode’ does not use a mode map but - instead manipulates the global map, this involves advising - ‘tab-bar--define-keys’. - -‘M-<tab>’ (‘magit-section-cycle-diffs’) - Cycle the visibility of diff-related sections in the current - buffer. - -‘S-<tab>’ (‘magit-section-cycle-global’) - Cycle the visibility of all sections in the current buffer. - -‘1’ (‘magit-section-show-level-1’) -‘2’ (‘magit-section-show-level-2’) -‘3’ (‘magit-section-show-level-3’) -‘4’ (‘magit-section-show-level-4’) - Show sections surrounding the current section up to level N. - -‘M-1’ (‘magit-section-show-level-1-all’) -‘M-2’ (‘magit-section-show-level-2-all’) -‘M-3’ (‘magit-section-show-level-3-all’) -‘M-4’ (‘magit-section-show-level-4-all’) - Show all sections up to level N. - - Some functions, which are used to implement the above commands, are -also exposed as commands themselves. By default no keys are bound to -these commands, as they are generally perceived to be much less useful. -But your mileage may vary. - - -- Command: magit-section-show - Show the body of the current section. - - -- Command: magit-section-hide - Hide the body of the current section. - - -- Command: magit-section-show-headings - Recursively show headings of children of the current section. Only - show the headings. Previously shown text-only bodies are hidden. - - -- Command: magit-section-show-children - Recursively show the bodies of children of the current section. - With a prefix argument show children down to the level of the - current section, and hide deeper children. - - -- Command: magit-section-hide-children - Recursively hide the bodies of children of the current section. - - -- Command: magit-section-toggle-children - Toggle visibility of bodies of children of the current section. - - When a buffer is first created then some sections are shown expanded -while others are not. This is hard coded. When a buffer is refreshed -then the previous visibility is preserved. The initial visibility of -certain sections can also be overwritten using the hook -‘magit-section-set-visibility-hook’. - - -- User Option: magit-section-initial-visibility-alist - This options can be used to override the initial visibility of - sections. In the future it will also be used to define the - defaults, but currently a section’s default is still hardcoded. - - The value is an alist. Each element maps a section type or lineage - to the initial visibility state for such sections. The state has - to be one of ‘show’ or ‘hide’, or a function that returns one of - these symbols. A function is called with the section as the only - argument. - - Use the command ‘magit-describe-section-briefly’ to determine a - section’s lineage or type. The vector in the output is the section - lineage and the type is the first element of that vector. - Wildcards can be used, see ‘magit-section-match’. - - -- User Option: magit-section-cache-visibility - This option controls for which sections the previous visibility - state should be restored if a section disappears and later appears - again. The value is a boolean or a list of section types. If t, - then the visibility of all sections is cached. Otherwise this is - only done for sections whose type matches one of the listed types. - - This requires that the function ‘magit-section-cached-visibility’ - is a member of ‘magit-section-set-visibility-hook’. - - -- Variable: magit-section-set-visibility-hook - This hook is run when first creating a buffer and also when - refreshing an existing buffer, and is used to determine the - visibility of the section currently being inserted. - - Each function is called with one argument, the section being - inserted. It should return ‘hide’ or ‘show’, or to leave the - visibility undefined ‘nil’. If no function decides on the - visibility and the buffer is being refreshed, then the visibility - is preserved; or if the buffer is being created, then the hard - coded default is used. - - Usually this should only be used to set the initial visibility but - not during refreshes. If ‘magit-insert-section--oldroot’ is - non-nil, then the buffer is being refreshed and these functions - should immediately return ‘nil’. - - -- User Option: magit-section-visibility-indicator - This option controls whether and how to indicate that a section can - be expanded/collapsed. - - If nil, then no visibility indicators are shown. Otherwise the - value has to have one of these two forms: - - • ‘(EXPANDABLE-BITMAP . COLLAPSIBLE-BITMAP)’ - - Both values have to be variables whose values are fringe - bitmaps. In this case every section that can be expanded or - collapsed gets an indicator in the left fringe. - - To provide extra padding around the indicator, set - ‘left-fringe-width’ in ‘magit-mode-hook’, e.g.: - - (add-hook 'magit-mode-hook (lambda () - (setq left-fringe-width 20))) - - • ‘(STRING . BOOLEAN)’ - - In this case STRING (usually an ellipsis) is shown at the end - of the heading of every collapsed section. Expanded sections - get no indicator. The cdr controls whether the appearance of - these ellipsis take section highlighting into account. Doing - so might potentially have an impact on performance, while not - doing so is kinda ugly. - - -File: magit.info, Node: Section Hooks, Next: Section Types and Values, Prev: Section Visibility, Up: Sections - -4.2.3 Section Hooks -------------------- - -Which sections are inserted into certain buffers is controlled with -hooks. This includes the status and the refs buffers. For other -buffers, e.g., log and diff buffers, this is not possible. The command -‘magit-describe-section’ can be used to see which hook (if any) was -responsible for inserting the section at point. - - For buffers whose sections can be customized by the user, a hook -variable called ‘magit-TYPE-sections-hook’ exists. This hook should be -changed using ‘magit-add-section-hook’. Avoid using ‘add-hooks’ or the -Custom interface. - - The various available section hook variables are described later in -this manual along with the appropriate "section inserter functions". - - -- Function: magit-add-section-hook hook function &optional at append - local - Add the function FUNCTION to the value of section hook HOOK. - - Add FUNCTION at the beginning of the hook list unless optional - APPEND is non-nil, in which case FUNCTION is added at the end. If - FUNCTION already is a member then move it to the new location. - - If optional AT is non-nil and a member of the hook list, then add - FUNCTION next to that instead. Add before or after AT, or replace - AT with FUNCTION depending on APPEND. If APPEND is the symbol - ‘replace’, then replace AT with FUNCTION. For any other non-nil - value place FUNCTION right after AT. If nil, then place FUNCTION - right before AT. If FUNCTION already is a member of the list but - AT is not, then leave FUNCTION where ever it already is. - - If optional LOCAL is non-nil, then modify the hook’s buffer-local - value rather than its global value. This makes the hook local by - copying the default value. That copy is then modified. - - HOOK should be a symbol. If HOOK is void, it is first set to nil. - HOOK’s value must not be a single hook function. FUNCTION should - be a function that takes no arguments and inserts one or multiple - sections at point, moving point forward. FUNCTION may choose not - to insert its section(s), when doing so would not make sense. It - should not be abused for other side-effects. - - To remove a function from a section hook, use ‘remove-hook’. - - -File: magit.info, Node: Section Types and Values, Next: Section Options, Prev: Section Hooks, Up: Sections - -4.2.4 Section Types and Values ------------------------------- - -Each section has a type, for example ‘hunk’, ‘file’, and ‘commit’. -Instances of certain section types also have a value. The value of a -section of type ‘file’, for example, is a file name. - - Users usually do not have to worry about a section’s type and value, -but knowing them can be handy at times. - -‘H’ (‘magit-describe-section’) - This command shows information about the section at point in a - separate buffer. - - -- Command: magit-describe-section-briefly - This command shows information about the section at point in the - echo area, as ‘#<magit-section VALUE [TYPE PARENT-TYPE...] - BEGINNING-END>’. - - Many commands behave differently depending on the type of the section -at point and/or somehow consume the value of that section. But that is -only one of the reasons why the same key may do something different, -depending on what section is current. - - Additionally for each section type a keymap *might* be defined, named -‘magit-TYPE-section-map’. That keymap is used as text property keymap -of all text belonging to any section of the respective type. If such a -map does not exist for a certain type, then you can define it yourself, -and it will automatically be used. - - -File: magit.info, Node: Section Options, Prev: Section Types and Values, Up: Sections - -4.2.5 Section Options ---------------------- - -This section describes options that have an effect on more than just a -certain type of sections. As you can see there are not many of those. - - -- User Option: magit-section-show-child-count - Whether to append the number of children to section headings. This - only affects sections that could benefit from this information. - - -File: magit.info, Node: Transient Commands, Next: Transient Arguments and Buffer Variables, Prev: Sections, Up: Interface Concepts - -4.3 Transient Commands -====================== - -Many Magit commands are implemented as *transient* commands. First the -user invokes a *prefix* command, which causes its *infix* arguments and -*suffix* commands to be displayed in the echo area. The user then -optionally sets some infix arguments and finally invokes one of the -suffix commands. - - This is implemented in the library ‘transient’. Earlier Magit -releases used the package ‘magit-popup’ and even earlier versions -library ‘magit-key-mode’. - - Transient is documented in *note (transient)Top::. - -‘C-x M-g’ (‘magit-dispatch’) -‘C-c g’ (‘magit-dispatch’) - This transient prefix command binds most of Magit’s other prefix - commands as suffix commands and displays them in a temporary buffer - until one of them is invoked. Invoking such a sub-prefix causes - the suffixes of that command to be bound and displayed instead of - those of ‘magit-dispatch’. - - This command is also, or especially, useful outside Magit buffers, - so Magit by default binds it to ‘C-c M-g’ in the global keymap. - ‘C-c g’ would be a better binding, but we cannot use that by - default, because that key sequence is reserved for the user. See - *note Global Bindings:: to learn more default and recommended key - bindings. - - -File: magit.info, Node: Transient Arguments and Buffer Variables, Next: Completion Confirmation and the Selection, Prev: Transient Commands, Up: Interface Concepts - -4.4 Transient Arguments and Buffer Variables -============================================ - -The infix arguments of many of Magit’s transient prefix commands cease -to have an effect once the ‘git’ command that is called with those -arguments has returned. Commands that create a commit are a good -example for this. If the user changes the arguments, then that only -affects the next invocation of a suffix command. If the same transient -prefix command is later invoked again, then the arguments are initially -reset to the default value. This default value can be set for the -current Emacs session or saved permanently, see *note (transient)Saving -Values::. It is also possible to cycle through previously used sets of -arguments using ‘C-M-p’ and ‘C-M-n’, see *note (transient)Using -History::. - - However the infix arguments of many other transient commands continue -to have an effect even after the ‘git’ command that was called with -those arguments has returned. The most important commands like this are -those that display a diff or log in a dedicated buffer. Their arguments -obviously continue to have an effect for as long as the respective diff -or log is being displayed. Furthermore the used arguments are stored in -buffer-local variables for future reference. - - For commands in the second group it isn’t always desirable to reset -their arguments to the global value when the transient prefix command is -invoked again. - - As mentioned above, it is possible to cycle through previously used -sets of arguments while a transient popup is visible. That means that -we could always reset the infix arguments to the default because the set -of arguments that is active in the existing buffer is only a few ‘C-M-p’ -away. Magit can be configured to behave like that, but because I expect -that most users would not find that very convenient, it is not the -default. - - Also note that it is possible to change the diff and log arguments -used in the current buffer (including the status buffer, which contains -both diff and log sections) using the respective "refresh" transient -prefix commands on ‘D’ and ‘L’. (‘d’ and ‘l’ on the other hand are -intended to change *what* diff or log is being displayed. It is -possible to also change *how* the diff or log is being displayed at the -same time, but if you only want to do the latter, then you should use -the refresh variants.) Because these secondary diff and log transient -prefixes are about *changing* the arguments used in the current buffer, -they *always* start out with the set of arguments that are currently in -effect in that buffer. - - Some commands are usually invoked directly even though they can also -be invoked as the suffix of a transient prefix command. Most -prominently ‘magit-show-commit’ is usually invoked by typing ‘RET’ while -point is on a commit in a log, but it can also be invoked from the -‘magit-diff’ transient prefix. - - When such a command is invoked directly, then it is important to -reuse the arguments as specified by the respective buffer-local values, -instead of using the default arguments. Imagine you press ‘RET’ in a -log to display the commit at point in a different buffer and then use -‘D’ to change how the diff is displayed in that buffer. And then you -press ‘RET’ on another commit to show that instead and the diff -arguments are reset to the default. Not cool; so Magit does not do that -by default. - - -- User Option: magit-prefix-use-buffer-arguments - This option controls whether the infix arguments initially shown in - certain transient prefix commands are based on the arguments that - are currently in effect in the buffer that their suffixes update. - - The ‘magit-diff’ and ‘magit-log’ transient prefix commands are - affected by this option. - - -- User Option: magit-direct-use-buffer-arguments - This option controls whether certain commands, when invoked - directly (i.e., not as the suffix of a transient prefix command), - use the arguments that are currently active in the buffer that they - are about to update. The alternative is to use the default value - for these arguments, which might change the arguments that are used - in the buffer. - -Valid values for both of the above options are: - - • ‘always’: Always use the set of arguments that is currently active - in the respective buffer, provided that buffer exists of course. - • ‘selected’ or ‘t’: Use the set of arguments from the respective - buffer, but only if it is displayed in a window of the current - frame. This is the default for both variables. - • ‘current’: Use the set of arguments from the respective buffer, but - only if it is the current buffer. - • ‘never’: Never use the set of arguments from the respective buffer. - -I am afraid it gets more complicated still: - - • The global diff and log arguments are set for each supported mode - individually. The diff arguments for example have different values - in ‘magit-diff-mode’, ‘magit-revision-mode’, - ‘magit-merge-preview-mode’ and ‘magit-status-mode’ buffers. - Setting or saving the value for one mode does not change the value - for other modes. The history however is shared. - - • When ‘magit-show-commit’ is invoked directly from a log buffer, - then the file filter is picked up from that buffer, not from the - revision buffer or the mode’s global diff arguments. - - • Even though they are suffixes of the diff prefix - ‘magit-show-commit’ and ‘magit-stash-show’ do not use the diff - buffer used by the diff commands, instead they use the dedicated - revision and stash buffers. - - At the time you invoke the diff prefix it is unknown to Magit which - of the suffix commands you are going to invoke. While not certain, - more often than not users invoke one of the commands that use the - diff buffer, so the initial infix arguments are those used in that - buffer. However if you invoke one of these commands directly, then - Magit knows that it should use the arguments from the revision - resp. stash buffer. - - • The log prefix also features reflog commands, but these commands do - not use the log arguments. - - • If ‘magit-show-refs’ is invoked from a ‘magit-refs-mode’ buffer, - then it acts as a refresh prefix and therefore unconditionally uses - the buffer’s arguments as initial arguments. If it is invoked - elsewhere with a prefix argument, then it acts as regular prefix - and therefore respects ‘magit-prefix-use-buffer-arguments’. If it - is invoked elsewhere without a prefix argument, then it acts as a - direct command and therefore respects - ‘magit-direct-use-buffer-arguments’. - - -File: magit.info, Node: Completion Confirmation and the Selection, Next: Mouse Support, Prev: Transient Arguments and Buffer Variables, Up: Interface Concepts - -4.5 Completion, Confirmation and the Selection -============================================== - -* Menu: - -* Action Confirmation:: -* Completion and Confirmation:: -* The Selection:: -* The hunk-internal region:: -* Support for Completion Frameworks:: -* Additional Completion Options:: - - -File: magit.info, Node: Action Confirmation, Next: Completion and Confirmation, Up: Completion Confirmation and the Selection - -4.5.1 Action Confirmation -------------------------- - -By default many actions that could potentially lead to data loss have to -be confirmed. This includes many very common actions, so this can -quickly become annoying. Many of these actions can be undone and if you -have thought about how to undo certain mistakes, then it should be safe -to disable confirmation for the respective actions. - - The option ‘magit-no-confirm’ can be used to tell Magit to perform -certain actions without the user having to confirm them. Note that -while this option can only be used to disable confirmation for a -specific set of actions, the next section explains another way of -telling Magit to ask fewer questions. - - -- User Option: magit-no-confirm - The value of this option is a list of symbols, representing actions - that do not have to be confirmed by the user before being carried - out. - - By default many potentially dangerous commands ask the user for - confirmation. Each of the below symbols stands for an action - which, when invoked unintentionally or without being fully aware of - the consequences, could lead to tears. In many cases there are - several commands that perform variations of a certain action, so we - don’t use the command names but more generic symbols. - - • Applying changes: - - • ‘discard’ Discarding one or more changes (i.e., hunks or - the complete diff for a file) loses that change, - obviously. - - • ‘reverse’ Reverting one or more changes can usually be - undone by reverting the reversion. - - • ‘stage-all-changes’, ‘unstage-all-changes’ When there are - both staged and unstaged changes, then un-/staging - everything would destroy that distinction. Of course - that also applies when un-/staging a single change, but - then less is lost and one does that so often that having - to confirm every time would be unacceptable. - - • Files: - - • ‘delete’ When a file that isn’t yet tracked by Git is - deleted, then it is completely lost, not just the last - changes. Very dangerous. - - • ‘trash’ Instead of deleting a file it can also be move to - the system trash. Obviously much less dangerous than - deleting it. - - Also see option ‘magit-delete-by-moving-to-trash’. - - • ‘resurrect’ A deleted file can easily be resurrected by - "deleting" the deletion, which is done using the same - command that was used to delete the same file in the - first place. - - • ‘untrack’ Untracking a file can be undone by tracking it - again. - - • ‘rename’ Renaming a file can easily be undone. - - • Sequences: - - • ‘reset-bisect’ Aborting (known to Git as "resetting") a - bisect operation loses all information collected so far. - - • ‘abort-cherry-pick’ Aborting a cherry-pick throws away - all conflict resolutions which have already been carried - out by the user. - - • ‘abort-revert’ Aborting a revert throws away all conflict - resolutions which have already been carried out by the - user. - - • ‘abort-rebase’ Aborting a rebase throws away all already - modified commits, but it’s possible to restore those from - the reflog. - - • ‘abort-merge’ Aborting a merge throws away all conflict - resolutions which have already been carried out by the - user. - - • ‘merge-dirty’ Merging with a dirty worktree can make it - hard to go back to the state before the merge was - initiated. - - • References: - - • ‘delete-unmerged-branch’ Once a branch has been deleted, - it can only be restored using low-level recovery tools - provided by Git. And even then the reflog is gone. The - user always has to confirm the deletion of a branch by - accepting the default choice (or selecting another - branch), but when a branch has not been merged yet, also - make sure the user is aware of that. - - • ‘delete-pr-remote’ When deleting a branch that was - created from a pull-request and if no other branches - still exist on that remote, then ‘magit-branch-delete’ - offers to delete the remote as well. This should be safe - because it only happens if no other refs exist in the - remotes namespace, and you can recreate the remote if - necessary. - - • ‘drop-stashes’ Dropping a stash is dangerous because Git - stores stashes in the reflog. Once a stash is removed, - there is no going back without using low-level recovery - tools provided by Git. When a single stash is dropped, - then the user always has to confirm by accepting the - default (or selecting another). This action only - concerns the deletion of multiple stashes at once. - - • Publishing: - - • ‘set-and-push’ When pushing to the upstream or the - push-remote and that isn’t actually configured yet, then - the user can first set the target. If s/he confirms the - default too quickly, then s/he might end up pushing to - the wrong branch and if the remote repository is - configured to disallow fixing such mistakes, then that - can be quite embarrassing and annoying. - - • Edit published history: - - Without adding these symbols here, you will be warned before - editing commits that have already been pushed to one of the - branches listed in ‘magit-published-branches’. - - • ‘amend-published’ Affects most commands that amend to - "HEAD". - - • ‘rebase-published’ Affects commands that perform - interactive rebases. This includes commands from the - commit transient that modify a commit other than "HEAD", - namely the various fixup and squash variants. - - • ‘edit-published’ Affects the commands - ‘magit-edit-line-commit’ and - ‘magit-diff-edit-hunk-commit’. These two commands make - it quite easy to accidentally edit a published commit, so - you should think twice before configuring them not to ask - for confirmation. - - To disable confirmation completely, add all three symbols here - or set ‘magit-published-branches’ to ‘nil’. - - • Various: - - • ‘stash-apply-3way’ When a stash cannot be applied using - ‘git stash apply’, then Magit uses ‘git apply’ instead, - possibly using the ‘--3way’ argument, which isn’t always - perfectly safe. See also ‘magit-stash-apply’. - - • ‘kill-process’ There seldom is a reason to kill a - process. - - • Global settings: - - Instead of adding all of the above symbols to the value of - this option, you can also set it to the atom ‘t’, which has - the same effect as adding all of the above symbols. Doing - that most certainly is a bad idea, especially because other - symbols might be added in the future. So even if you don’t - want to be asked for confirmation for any of these actions, - you are still better of adding all of the respective symbols - individually. - - When ‘magit-wip-before-change-mode’ is enabled, then the - following actions can be undone fairly easily: ‘discard’, - ‘reverse’, ‘stage-all-changes’, and ‘unstage-all-changes’. If - and only if this mode is enabled, then ‘safe-with-wip’ has the - same effect as adding all of these symbols individually. - - -File: magit.info, Node: Completion and Confirmation, Next: The Selection, Prev: Action Confirmation, Up: Completion Confirmation and the Selection - -4.5.2 Completion and Confirmation ---------------------------------- - -Many Magit commands ask the user to select from a list of possible -things to act on, while offering the most likely choice as the default. -For many of these commands the default is the thing at point, provided -that it actually is a valid thing to act on. For many commands that act -on a branch, the current branch serves as the default if there is no -branch at point. - - These commands combine asking for confirmation and asking for a -target to act on into a single action. The user can confirm the default -target using ‘RET’ or abort using ‘C-g’. This is similar to a -‘y-or-n-p’ prompt, but the keys to confirm or abort differ. - - At the same time the user is also given the opportunity to select -another target, which is useful because for some commands and/or in some -situations you might want to select the action before selecting the -target by moving to it. - - However you might find that for some commands you always want to use -the default target, if any, or even that you want the command to act on -the default without requiring any confirmation at all. The option -‘magit-dwim-selection’ can be used to configure certain commands to that -effect. - - Note that when the region is active then many commands act on the -things that are selected using a mechanism based on the region, in many -cases after asking for confirmation. This region-based mechanism is -called the "selection" and is described in detail in the next section. -When a selection exists that is valid for the invoked command, then that -command never offers to act on something else, and whether it asks for -confirmation is not controlled by this option. - - Also note that Magit asks for confirmation of certain actions that -are not coupled with completion (or the selection). Such dialogs are -also not affected by this option and are described in the previous -section. - - -- User Option: magit-dwim-selection - This option can be used to tell certain commands to use the thing at -point instead of asking the user to select a candidate to act on, with -or without confirmation. - - The value has the form ‘((COMMAND nil|PROMPT DEFAULT)...)’. - - • COMMAND is the command that should not prompt for a choice. To - have an effect, the command has to use the function - ‘magit-completing-read’ or a utility function which in turn uses - that function. - - • If the command uses ‘magit-completing-read’ multiple times, then - PROMPT can be used to only affect one of these uses. PROMPT, if - non-nil, is a regular expression that is used to match against the - PROMPT argument passed to ‘magit-completing-read’. - - • DEFAULT specifies how to use the default. If it is ‘t’, then the - DEFAULT argument passed to ‘magit-completing-read’ is used without - confirmation. If it is ‘ask’, then the user is given a chance to - abort. DEFAULT can also be ‘nil’, in which case the entry has no - effect. - - -File: magit.info, Node: The Selection, Next: The hunk-internal region, Prev: Completion and Confirmation, Up: Completion Confirmation and the Selection - -4.5.3 The Selection -------------------- - -If the region is active, then many Magit commands act on the things that -are selected using a mechanism based on the region instead of one single -thing. When the region is not active, then these commands act on the -thing at point or read a single thing to act on. This is described in -the previous section — this section only covers how multiple things are -selected, how that is visualized, and how certain commands behave when -that is the case. - - Magit’s mechanism for selecting multiple things, or rather sections -that represent these things, is based on the Emacs region, but the area -that Magit considers to be selected is typically larger than the region -and additional restrictions apply. - - Magit makes a distinction between a region that qualifies as forming -a valid Magit selection and a region that does not. If the region does -not qualify, then it is displayed as it is in other Emacs buffers. If -the region does qualify as a Magit selection, then the selection is -always visualized, while the region itself is only visualized if it -begins and ends on the same line. - - For a region to qualify as a Magit selection, it must begin in the -heading of one section and end in the heading of a sibling section. -Note that if the end of the region is at the very beginning of section -heading (i.e., at the very beginning of a line) then that section is -considered to be *inside* the selection. - - This is not consistent with how the region is normally treated in -Emacs — if the region ends at the beginning of a line, then that line is -outside the region. Due to how Magit visualizes the selection, it -should be obvious that this difference exists. - - Not every command acts on every valid selection. Some commands do -not even consider the location of point, others may act on the section -at point but not support acting on the selection, and even commands that -do support the selection of course only do so if it selects things that -they can act on. - - This is the main reason why the selection must include the section at -point. Even if a selection exists, the invoked command may disregard -it, in which case it may act on the current section only. It is much -safer to only act on the current section but not the other selected -sections than it is to act on the current section *instead* of the -selected sections. The latter would be much more surprising and if the -current section always is part of the selection, then that cannot -happen. - - -- Variable: magit-keep-region-overlay - This variable controls whether the region is visualized as usual - even when a valid Magit selection or a hunk-internal region exists. - See the doc-string for more information. - - -File: magit.info, Node: The hunk-internal region, Next: Support for Completion Frameworks, Prev: The Selection, Up: Completion Confirmation and the Selection - -4.5.4 The hunk-internal region ------------------------------- - -Somewhat related to the Magit selection described in the previous -section is the hunk-internal region. - - Like the selection, the hunk-internal region is based on the Emacs -region but causes that region to not be visualized as it would in other -Emacs buffers, and includes the line on which the region ends even if it -ends at the very beginning of that line. - - Unlike the selection, which is based on a region that must begin in -the heading of one section and ends in the section of a sibling section, -the hunk-internal region must begin inside the *body* of a hunk section -and end in the body of the *same* section. - - The hunk-internal region is honored by "apply" commands, which can, -among other targets, act on a hunk. If the hunk-internal region is -active, then such commands act only on the marked part of the hunk -instead of on the complete hunk. - - -File: magit.info, Node: Support for Completion Frameworks, Next: Additional Completion Options, Prev: The hunk-internal region, Up: Completion Confirmation and the Selection - -4.5.5 Support for Completion Frameworks ---------------------------------------- - -The built-in option ‘completing-read-function’ specifies the low-level -function used by ‘completing-read’ to ask a user to select from a list -of choices. Its default value is ‘completing-read-default’. -Alternative completion frameworks typically activate themselves by -substituting their own implementation. - - Mostly for historic reasons Magit provides a similar option named -‘magit-completing-read-function’, which only controls the low-level -function used by ‘magit-completing-read’. This option also makes it -possible to use a different completing mechanism for Magit than for the -rest of Emacs, but doing that is not recommend. - - You most likely don’t have to customize the magit-specific option to -use an alternative completion framework. For example, if you enable -‘ivy-mode’, then Magit will respect that, and if you enable ‘helm-mode’, -then you are done too. - - However if you want to use Ido, then ‘ido-mode’ won’t do the trick. -You will also have to install the ‘ido-completing-read+’ package and use -‘magit-ido-completing-read’ as ‘magit-completing-read-function’. - - -- User Option: magit-completing-read-function - The value of this variable is the low-level function used to - perform completion by code that uses ‘magit-completing-read’ (as - opposed to the built-in ‘completing-read’). - - The default value, ‘magit-builtin-completing-read’, is suitable for - the standard completion mechanism, ‘ivy-mode’, and ‘helm-mode’ at - least. - - The built-in ‘completing-read’ and ‘completing-read-default’ are - *not* suitable to be used here. ‘magit-builtin-completing-read’ - performs some additional work, and any function used in its place - has to do the same. - - -- Function: magit-builtin-completing-read prompt choices &optional - predicate require-match initial-input hist def - This function performs completion using the built-in - ‘completing-read’ and does some additional magit-specific work. - - -- Function: magit-ido-completing-read prompt choices &optional - predicate require-match initial-input hist def - This function performs completion using ‘ido-completing-read+’ from - the package by the same name (which you have to explicitly install) - and does some additional magit-specific work. - - We have to use ‘ido-completing-read+’ instead of the - ‘ido-completing-read’ that comes with Ido itself, because the - latter, while intended as a drop-in replacement, cannot serve that - purpose because it violates too many of the implicit conventions. - - -- Function: magit-completing-read prompt choices &optional predicate - require-match initial-input hist def fallback - This is the function that Magit commands use when they need the - user to select a single thing to act on. The arguments have the - same meaning as for ‘completing-read’, except for FALLBACK, which - is unique to this function and is described below. - - Instead of asking the user to choose from a list of possible - candidates, this function may just return the default specified by - DEF, with or without requiring user confirmation. Whether that is - the case depends on PROMPT, ‘this-command’ and - ‘magit-dwim-selection’. See the documentation of the latter for - more information. - - If it does read a value in the minibuffer, then this function acts - similar to ‘completing-read’, except for the following: - - • COLLECTION must be a list of choices. A function is not - supported. - - • If REQUIRE-MATCH is ‘nil’ and the user exits without a choice, - then ‘nil’ is returned instead of an empty string. - - • If REQUIRE-MATCH is non-nil and the users exits without a - choice, an user-error is raised. - - • FALLBACK specifies a secondary default that is only used if - the primary default DEF is ‘nil’. The secondary default is - not subject to ‘magit-dwim-selection’ — if DEF is ‘nil’ but - FALLBACK is not, then this function always asks the user to - choose a candidate, just as if both defaults were ‘nil’. - - • ‘format-prompt’ is called on PROMPT and DEF (or FALLBACK if - DEF is ‘nil’). This appends ": " to the prompt and may also - add the default to the prompt, using the format specified by - ‘minibuffer-default-prompt-format’ and depending on - ‘magit-completing-read-default-prompt-predicate’. - - -File: magit.info, Node: Additional Completion Options, Prev: Support for Completion Frameworks, Up: Completion Confirmation and the Selection - -4.5.6 Additional Completion Options ------------------------------------ - - -- User Option: magit-list-refs-sortby - For many commands that read a ref or refs from the user, the value - of this option can be used to control the order of the refs. Valid - values include any key accepted by the ‘--sort’ flag of ‘git - for-each-ref’. By default, refs are sorted alphabetically by their - full name (e.g., "refs/heads/master"). - - -File: magit.info, Node: Mouse Support, Next: Running Git, Prev: Completion Confirmation and the Selection, Up: Interface Concepts - -4.6 Mouse Support -================= - -Double clicking on a section heading toggles the visibility of its body, -if any. Likewise clicking in the left fringe toggles the visibility of -the appropriate section. - - A context menu is provided but has to be enabled explicitly. In -Emacs 28 and greater, enable the global mode ‘context-menu-mode’. If -you use an older Emacs release, set -‘magit-section-show-context-menu-for-emacs<28’. - - -File: magit.info, Node: Running Git, Prev: Mouse Support, Up: Interface Concepts - -4.7 Running Git -=============== - -* Menu: - -* Viewing Git Output:: -* Git Process Status:: -* Running Git Manually:: -* Git Executable:: -* Global Git Arguments:: - - -File: magit.info, Node: Viewing Git Output, Next: Git Process Status, Up: Running Git - -4.7.1 Viewing Git Output ------------------------- - -Magit runs Git either for side-effects (e.g., when pushing) or to get -some value (e.g., the name of the current branch). - - When Git is run for side-effects, the process output is logged in a -per-repository log buffer, which can be consulted using the -‘magit-process’ command when things don’t go as expected. - - The output/errors for up to ‘magit-process-log-max’ Git commands are -retained. - -‘$’ (‘magit-process’) - This commands displays the process buffer for the current - repository. - - Inside that buffer, the usual key bindings for navigating and showing -sections are available. There is one additional command. - -‘k’ (‘magit-process-kill’) - This command kills the process represented by the section at point. - - -- Variable: magit-git-debug - This option controls whether additional reporting of git errors is - enabled. - - Magit basically calls git for one of these two reasons: for - side-effects or to do something with its standard output. - - When git is run for side-effects then its output, including error - messages, go into the process buffer which is shown when using ‘$’. - - When git’s output is consumed in some way, then it would be too - expensive to also insert it into this buffer, but when this option - is non-nil and git returns with a non-zero exit status, then at - least its standard error is inserted into this buffer. - - This is only intended for debugging purposes. Do not enable this - permanently, that would negatively affect performance. - - This is only intended for debugging purposes. Do not enable this - permanently, that would negatively affect performance. Also note - that just because git exits with a non-zero exit status and prints - an error message that usually doesn’t mean that it is an error as - far as Magit is concerned, which is another reason we usually hide - these error messages. Whether some error message is relevant in - the context of some unexpected behavior has to be judged on a case - by case basis. - - The command ‘magit-toggle-git-debug’ changes the value of this - variable. - - -- Variable: magit-process-extreme-logging - This option controls whether ‘magit-process-file’ logs to the - ‘*Messages*’ buffer. - - Only intended for temporary use when you try to figure out how - Magit uses Git behind the scene. Output that normally goes to the - magit-process buffer continues to go there. Not all output goes to - either of these two buffers. - - -File: magit.info, Node: Git Process Status, Next: Running Git Manually, Prev: Viewing Git Output, Up: Running Git - -4.7.2 Git Process Status ------------------------- - -When a Git process is running for side-effects, Magit displays an -indicator in the mode line, using the ‘magit-mode-line-process’ face. - - If the Git process exits successfully, the process indicator is -removed from the mode line immediately. - - In the case of a Git error, the process indicator is not removed, but -is instead highlighted with the ‘magit-mode-line-process-error’ face, -and the error details from the process buffer are provided as a tooltip -for mouse users. This error indicator persists in the mode line until -the next magit buffer refresh. - - If you do not wish process errors to be indicated in the mode line, -customize the ‘magit-process-display-mode-line-error’ user option. - - Process errors are additionally indicated at the top of the status -buffer. - - -File: magit.info, Node: Running Git Manually, Next: Git Executable, Prev: Git Process Status, Up: Running Git - -4.7.3 Running Git Manually --------------------------- - -While Magit provides many Emacs commands to interact with Git, it does -not cover everything. In those cases your existing Git knowledge will -come in handy. Magit provides some commands for running arbitrary Git -commands by typing them into the minibuffer, instead of having to switch -to a shell. - -‘!’ (‘magit-run’) - This transient prefix command binds the following suffix commands - and displays them in a temporary buffer until a suffix is invoked. - -‘! !’ (‘magit-git-command-topdir’) - This command reads a command from the user and executes it in the - top-level directory of the current working tree. - - The string "git " is used as initial input when prompting the user - for the command. It can be removed to run another command. - -‘:’ (‘magit-git-command’) -‘! p’ - This command reads a command from the user and executes it in - ‘default-directory’. With a prefix argument the command is - executed in the top-level directory of the current working tree - instead. - - The string "git " is used as initial input when prompting the user - for the command. It can be removed to run another command. - -‘! s’ (‘magit-shell-command-topdir’) - This command reads a command from the user and executes it in the - top-level directory of the current working tree. - -‘! S’ (‘magit-shell-command’) - This command reads a command from the user and executes it in - ‘default-directory’. With a prefix argument the command is - executed in the top-level directory of the current working tree - instead. - - -- User Option: magit-shell-command-verbose-prompt - Whether the prompt, used by the above commands when reading a shell - command, shows the directory in which it will be run. - - These suffix commands start external gui tools. - -‘! k’ (‘magit-run-gitk’) - This command runs ‘gitk’ in the current repository. - -‘! a’ (‘magit-run-gitk-all’) - This command runs ‘gitk --all’ in the current repository. - -‘! b’ (‘magit-run-gitk-branches’) - This command runs ‘gitk --branches’ in the current repository. - -‘! g’ (‘magit-run-git-gui’) - This command runs ‘git gui’ in the current repository. - -‘! m’ (‘magit-git-mergetool’) - This command runs ‘git mergetool --gui’ in the current repository. - - With a prefix argument this acts as a transient prefix command, - allowing the user to select the mergetool and change some settings. - - -File: magit.info, Node: Git Executable, Next: Global Git Arguments, Prev: Running Git Manually, Up: Running Git - -4.7.4 Git Executable --------------------- - -When Magit calls Git, then it may do so using the absolute path to the -‘git’ executable, or using just its name. - - When running ‘git’ locally and the ‘system-type’ is ‘windows-nt’ (any -Windows version) or ‘darwin’ (macOS) then ‘magit-git-executable’ is set -to an absolute path when Magit is loaded. - - On Windows it is necessary to use an absolute path because Git comes -with several wrapper scripts for the actual ‘git’ binary, which are also -placed on ‘$PATH’, and using one of these wrappers instead of the binary -would degrade performance horribly. For some macOS users using just the -name of the executable also performs horribly, so we avoid doing that on -that platform as well. On other platforms, using just the name seems to -work just fine. - - Using an absolute path when running ‘git’ on a remote machine over -Tramp, would be problematic to use an absolute path that is suitable on -the local machine, so a separate option is used to control the name or -path that is used on remote machines. - - -- User Option: magit-git-executable - The ‘git’ executable used by Magit on the local host. This should - be either the absolute path to the executable, or the string "git" - to let Emacs find the executable itself, using the standard - mechanism for doing such things. - - -- User Option: magit-remote-git-executable - The ‘git’ executable used by Magit on remote machines over Tramp. - Normally this should be just the string "git". Consider - customizing ‘tramp-remote-path’ instead of this option. - - If Emacs is unable to find the correct executable, then you can work -around that by explicitly setting the value of one of these two options. -Doing that should be considered a kludge; it is better to make sure that -the order in ‘exec-path’ or ‘tramp-remote-path’ is correct. - - Note that ‘exec-path’ is set based on the value of the ‘PATH’ -environment variable that is in effect when Emacs is started. If you -set ‘PATH’ in your shell’s init files, then that only has an effect on -Emacs if you start it from that shell (because the environment of a -process is only passed to its child processes, not to arbitrary other -processes). If that is not how you start Emacs, then the -‘exec-path-from-shell’ package can help; though honestly I consider that -a kludge too. - - The command ‘magit-debug-git-executable’ can be useful to find out -where Emacs is searching for ‘git’. - -‘M-x magit-debug-git-executable’ - This command displays a buffer with information about - ‘magit-git-executable’ and ‘magit-remote-git-executable’. - -‘M-x magit-version’ - This command shows the currently used versions of Magit, Git, and - Emacs in the echo area. Non-interactively this just returns the - Magit version. - - -File: magit.info, Node: Global Git Arguments, Prev: Git Executable, Up: Running Git - -4.7.5 Global Git Arguments --------------------------- - - -- User Option: magit-git-global-arguments - The arguments set here are used every time the git executable is - run as a subprocess. They are placed right after the executable - itself and before the git command - as in ‘git HERE... COMMAND - REST’. For valid arguments see *note (gitman)git::. - - Be careful what you add here, especially if you are using Tramp to - connect to servers with ancient Git versions. Never remove - anything that is part of the default value, unless you really know - what you are doing. And think very hard before adding something; - it will be used every time Magit runs Git for any purpose. - - -File: magit.info, Node: Inspecting, Next: Manipulating, Prev: Interface Concepts, Up: Top - -5 Inspecting -************ - -The functionality provided by Magit can be roughly divided into three -groups: inspecting existing data, manipulating existing data or adding -new data, and transferring data. Of course that is a rather crude -distinction that often falls short, but it’s more useful than no -distinction at all. This section is concerned with inspecting data, the -next two with manipulating and transferring it. Then follows a section -about miscellaneous functionality, which cannot easily be fit into this -distinction. - - Of course other distinctions make sense too, e.g., Git’s distinction -between porcelain and plumbing commands, which for the most part is -equivalent to Emacs’ distinction between interactive commands and -non-interactive functions. All of the sections mentioned before are -mainly concerned with the porcelain – Magit’s plumbing layer is -described later. - -* Menu: - -* Status Buffer:: -* Repository List:: -* Logging:: -* Diffing:: -* Ediffing:: -* References Buffer:: -* Bisecting:: -* Visiting Files and Blobs:: -* Blaming:: - - -File: magit.info, Node: Status Buffer, Next: Repository List, Up: Inspecting - -5.1 Status Buffer -================= - -While other Magit buffers contain, e.g., one particular diff or one -particular log, the status buffer contains the diffs for staged and -unstaged changes, logs for unpushed and unpulled commits, lists of -stashes and untracked files, and information related to the current -branch. - - During certain incomplete operations – for example when a merge -resulted in a conflict – additional information is displayed that helps -proceeding with or aborting the operation. - - The command ‘magit-status’ displays the status buffer belonging to -the current repository in another window. This command is used so often -that it should be bound globally. We recommend using ‘C-x g’: - - (global-set-key (kbd "C-x g") 'magit-status) - -‘C-x g’ (‘magit-status’) - When invoked from within an existing Git repository, then this - command shows the status of that repository in a buffer. - - If the current directory isn’t located within a Git repository, - then this command prompts for an existing repository or an - arbitrary directory, depending on the option - ‘magit-repository-directories’, and the status for the selected - repository is shown instead. - - • If that option specifies any existing repositories, then the - user is asked to select one of them. - - • Otherwise the user is asked to select an arbitrary directory - using regular file-name completion. If the selected directory - is the top-level directory of an existing working tree, then - the status buffer for that is shown. - - • Otherwise the user is offered to initialize the selected - directory as a new repository. After creating the repository - its status buffer is shown. - - These fallback behaviors can also be forced using one or more - prefix arguments: - - • With two prefix arguments (or more precisely a numeric prefix - value of 16 or greater) an arbitrary directory is read, which - is then acted on as described above. The same could be - accomplished using the command ‘magit-init’. - - • With a single prefix argument an existing repository is read - from the user, or if no repository can be found based on the - value of ‘magit-repository-directories’, then the behavior is - the same as with two prefix arguments. - - -- User Option: magit-repository-directories - List of directories that are Git repositories or contain Git - repositories. - - Each element has the form ‘(DIRECTORY . DEPTH)’. DIRECTORY has to - be a directory or a directory file-name, a string. DEPTH, an - integer, specifies the maximum depth to look for Git repositories. - If it is 0, then only add DIRECTORY itself. - - This option controls which repositories are being listed by - ‘magit-list-repositories’. It also affects ‘magit-status’ (which - see) in potentially surprising ways (see above). - - -- Command: magit-status-quick - This command is an alternative to ‘magit-status’ that usually - avoids refreshing the status buffer. - - If the status buffer of the current Git repository exists but isn’t - being displayed in the selected frame, then it is displayed without - being refreshed. - - If the status buffer is being displayed in the selected frame, then - this command refreshes it. - - Prefix arguments have the same meaning as for ‘magit-status’, and - additionally cause the buffer to be refresh. - - To use this command add this to your init file: - - (global-set-key (kbd "C-x g") 'magit-status-quick). - - If you do that and then for once want to redisplay the buffer and - also immediately refresh it, then type ‘C-x g’ followed by ‘g’. - - A possible alternative command is - ‘magit-display-repository-buffer’. It supports displaying any - existing Magit buffer that belongs to the current repository; not - just the status buffer. - - -- Command: ido-enter-magit-status - From an Ido prompt used to open a file, instead drop into - ‘magit-status’. This is similar to ‘ido-magic-delete-char’, which, - despite its name, usually causes a Dired buffer to be created. - - To make this command available, use something like: - - (add-hook 'ido-setup-hook - (lambda () - (define-key ido-completion-map - (kbd \"C-x g\") 'ido-enter-magit-status))) - - Starting with Emacs 25.1 the Ido keymaps are defined just once - instead of every time Ido is invoked, so now you can modify it like - pretty much every other keymap: - - (define-key ido-common-completion-map - (kbd \"C-x g\") 'ido-enter-magit-status) - -* Menu: - -* Status Sections:: -* Status Header Sections:: -* Status Module Sections:: -* Status Options:: - - -File: magit.info, Node: Status Sections, Next: Status Header Sections, Up: Status Buffer - -5.1.1 Status Sections ---------------------- - -The contents of status buffers is controlled using the hook -‘magit-status-sections-hook’. See *note Section Hooks:: to learn about -such hooks and how to customize them. - - -- User Option: magit-status-sections-hook - Hook run to insert sections into a status buffer. - - The first function on that hook by default is -‘magit-insert-status-headers’; it is described in the next section. By -default the following functions are also members of that hook: - - -- Function: magit-insert-merge-log - Insert section for the on-going merge. Display the heads that are - being merged. If no merge is in progress, do nothing. - - -- Function: magit-insert-rebase-sequence - Insert section for the on-going rebase sequence. If no such - sequence is in progress, do nothing. - - -- Function: magit-insert-am-sequence - Insert section for the on-going patch applying sequence. If no - such sequence is in progress, do nothing. - - -- Function: magit-insert-sequencer-sequence - Insert section for the on-going cherry-pick or revert sequence. If - no such sequence is in progress, do nothing. - - -- Function: magit-insert-bisect-output - While bisecting, insert section with output from ‘git bisect’. - - -- Function: magit-insert-bisect-rest - While bisecting, insert section visualizing the bisect state. - - -- Function: magit-insert-bisect-log - While bisecting, insert section logging bisect progress. - - -- Function: magit-insert-untracked-files - Maybe insert a list or tree of untracked files. - - Do so depending on the value of ‘status.showUntrackedFiles’. Note - that even if the value is ‘all’, Magit still initially only shows - directories. But the directory sections can then be expanded using - ‘TAB’. - - -- Function: magit-insert-unstaged-changes - Insert section showing unstaged changes. - - -- Function: magit-insert-staged-changes - Insert section showing staged changes. - - -- Function: magit-insert-stashes &optional ref heading - Insert the ‘stashes’ section showing reflog for "refs/stash". If - optional REF is non-nil show reflog for that instead. If optional - HEADING is non-nil use that as section heading instead of - "Stashes:". - - -- Function: magit-insert-unpulled-from-upstream - Insert section showing commits that haven’t been pulled from the - upstream branch yet. - - -- Function: magit-insert-unpulled-from-pushremote - Insert section showing commits that haven’t been pulled from the - push-remote branch yet. - - -- Function: magit-insert-unpushed-to-upstream - Insert section showing commits that haven’t been pushed to the - upstream yet. - - -- Function: magit-insert-unpushed-to-pushremote - Insert section showing commits that haven’t been pushed to the - push-remote yet. - - The following functions can also be added to the above hook: - - -- Function: magit-insert-tracked-files - Insert a tree of tracked files. - - -- Function: magit-insert-ignored-files - Insert a tree of ignored files. Its possible to limit the logs in - the current buffer to a certain directory using ‘D = f <DIRECTORY> - RET g’. If you do that, then that that also affects this command. - - The log filter can be used to limit to multiple files. In that - case this function only respects the first of the files and only if - it is a directory. - - -- Function: magit-insert-skip-worktree-files - Insert a tree of skip-worktree files. If the first element of - ‘magit-buffer-diff-files’ is a directory, then limit the list to - files below that. The value of that variable can be set using ‘D - -- DIRECTORY RET g’. - - -- Function: magit-insert-assumed-unchanged-files - Insert a tree of files that are assumed to be unchanged. If the - first element of ‘magit-buffer-diff-files’ is a directory, then - limit the list to files below that. The value of that variable can - be set using ‘D -- DIRECTORY RET g’. - - -- Function: magit-insert-unpulled-or-recent-commits - Insert section showing unpulled or recent commits. If an upstream - is configured for the current branch and it is ahead of the current - branch, then show the missing commits. Otherwise, show the last - ‘magit-log-section-commit-count’ commits. - - -- Function: magit-insert-recent-commits - Insert section showing the last ‘magit-log-section-commit-count’ - commits. - - -- User Option: magit-log-section-commit-count - How many recent commits ‘magit-insert-recent-commits’ and - ‘magit-insert-unpulled-or-recent-commits’ (provided there are no - unpulled commits) show. - - -- Function: magit-insert-unpulled-cherries - Insert section showing unpulled commits. Like - ‘magit-insert-unpulled-commits’ but prefix each commit that has not - been applied yet (i.e., a commit with a patch-id not shared with - any local commit) with "+", and all others with "-". - - -- Function: magit-insert-unpushed-cherries - Insert section showing unpushed commits. Like - ‘magit-insert-unpushed-commits’ but prefix each commit which has - not been applied to upstream yet (i.e., a commit with a patch-id - not shared with any upstream commit) with "+" and all others with - "-". - - See *note References Buffer:: for some more section inserters, which -could be used here. - - -File: magit.info, Node: Status Header Sections, Next: Status Module Sections, Prev: Status Sections, Up: Status Buffer - -5.1.2 Status Header Sections ----------------------------- - -The contents of status buffers is controlled using the hook -‘magit-status-sections-hook’ (see *note Status Sections::). - - By default ‘magit-insert-status-headers’ is the first member of that -hook variable. - - -- Function: magit-insert-status-headers - Insert headers sections appropriate for ‘magit-status-mode’ - buffers. The sections are inserted by running the functions on the - hook ‘magit-status-headers-hook’. - - -- User Option: magit-status-headers-hook - Hook run to insert headers sections into the status buffer. - - This hook is run by ‘magit-insert-status-headers’, which in turn - has to be a member of ‘magit-status-sections-hook’ to be used at - all. - - By default the following functions are members of the above hook: - - -- Function: magit-insert-error-header - Insert a header line showing the message about the Git error that - just occurred. - - This function is only aware of the last error that occur when Git - was run for side-effects. If, for example, an error occurs while - generating a diff, then that error won’t be inserted. Refreshing - the status buffer causes this section to disappear again. - - -- Function: magit-insert-diff-filter-header - Insert a header line showing the effective diff filters. - - -- Function: magit-insert-head-branch-header - Insert a header line about the current branch or detached ‘HEAD’. - - -- Function: magit-insert-upstream-branch-header - Insert a header line about the branch that is usually pulled into - the current branch. - - -- Function: magit-insert-push-branch-header - Insert a header line about the branch that the current branch is - usually pushed to. - - -- Function: magit-insert-tags-header - Insert a header line about the current and/or next tag, along with - the number of commits between the tag and ‘HEAD’. - - The following functions can also be added to the above hook: - - -- Function: magit-insert-repo-header - Insert a header line showing the path to the repository top-level. - - -- Function: magit-insert-remote-header - Insert a header line about the remote of the current branch. - - If no remote is configured for the current branch, then fall back - showing the "origin" remote, or if that does not exist the first - remote in alphabetic order. - - -- Function: magit-insert-user-header - Insert a header line about the current user. - - -File: magit.info, Node: Status Module Sections, Next: Status Options, Prev: Status Header Sections, Up: Status Buffer - -5.1.3 Status Module Sections ----------------------------- - -The contents of status buffers is controlled using the hook -‘magit-status-sections-hook’ (see *note Status Sections::). - - By default ‘magit-insert-modules’ is _not_ a member of that hook -variable. - - -- Function: magit-insert-modules - Insert submodule sections. - - Hook ‘magit-module-sections-hook’ controls which module sections - are inserted, and option ‘magit-module-sections-nested’ controls - whether they are wrapped in an additional section. - - -- User Option: magit-module-sections-hook - Hook run by ‘magit-insert-modules’. - - -- User Option: magit-module-sections-nested - This option controls whether ‘magit-insert-modules’ wraps inserted - sections in an additional section. - - If this is non-nil, then only a single top-level section is - inserted. If it is nil, then all sections listed in - ‘magit-module-sections-hook’ become top-level sections. - - -- Function: magit-insert-modules-overview - Insert sections for all submodules. For each section insert the - path, the branch, and the output of ‘git describe --tags’, or, - failing that, the abbreviated HEAD commit hash. - - Press ‘RET’ on such a submodule section to show its own status - buffer. Press ‘RET’ on the "Modules" section to display a list of - submodules in a separate buffer. This shows additional information - not displayed in the super-repository’s status buffer. - - -- Function: magit-insert-modules-unpulled-from-upstream - Insert sections for modules that haven’t been pulled from the - upstream yet. These sections can be expanded to show the - respective commits. - - -- Function: magit-insert-modules-unpulled-from-pushremote - Insert sections for modules that haven’t been pulled from the - push-remote yet. These sections can be expanded to show the - respective commits. - - -- Function: magit-insert-modules-unpushed-to-upstream - Insert sections for modules that haven’t been pushed to the - upstream yet. These sections can be expanded to show the - respective commits. - - -- Function: magit-insert-modules-unpushed-to-pushremote - Insert sections for modules that haven’t been pushed to the - push-remote yet. These sections can be expanded to show the - respective commits. - - -File: magit.info, Node: Status Options, Prev: Status Module Sections, Up: Status Buffer - -5.1.4 Status Options --------------------- - - -- User Option: magit-status-margin - This option specifies whether the margin is initially shown in - Magit-Status mode buffers and how it is formatted. - - The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. - - • If INIT is non-nil, then the margin is shown initially. - • STYLE controls how to format the author or committer date. It - can be one of ‘age’ (to show the age of the commit), - ‘age-abbreviated’ (to abbreviate the time unit to a - character), or a string (suitable for ‘format-time-string’) to - show the actual date. Option - ‘magit-log-margin-show-committer-date’ controls which date is - being displayed. - • WIDTH controls the width of the margin. This exists for - forward compatibility and currently the value should not be - changed. - • AUTHOR controls whether the name of the author is also shown - by default. - • AUTHOR-WIDTH has to be an integer. When the name of the - author is shown, then this specifies how much space is used to - do so. - - Also see the proceeding section for more options concerning status -buffers. - - -File: magit.info, Node: Repository List, Next: Logging, Prev: Status Buffer, Up: Inspecting - -5.2 Repository List -=================== - - -- Command: magit-list-repositories - This command displays a list of repositories in a separate buffer. - - The option ‘magit-repository-directories’ controls which - repositories are displayed. - - -- User Option: magit-repolist-columns - This option controls what columns are displayed by the command - ‘magit-list-repositories’ and how they are displayed. - - Each element has the form ‘(HEADER WIDTH FORMAT PROPS)’. - - HEADER is the string displayed in the header. WIDTH is the width - of the column. FORMAT is a function that is called with one - argument, the repository identification (usually its basename), and - with ‘default-directory’ bound to the toplevel of its working tree. - It has to return a string to be inserted or nil. PROPS is an alist - that supports the keys ‘:right-align’, ‘:pad-right’ and ‘:sort’. - - The ‘:sort’ function has a weird interface described in the - docstring of ‘tabulated-list--get-sort’. Alternatively ‘<’ and - ‘magit-repolist-version<’ can be used as those functions are - automatically replaced with functions that satisfy the interface. - Set ‘:sort’ to ‘nil’ to inhibit sorting; if unspecified, then the - column is sortable using the default sorter. - - You may wish to display a range of numeric columns using just one - character per column and without any padding between columns, in - which case you should use an appropriate HEADER, set WIDTH to 1, - and set ‘:pad-right’ to 9. ‘+’ is substituted for numbers higher - than 9. - -The following functions can be added to the above option: - - -- Function: magit-repolist-column-ident - This function inserts the identification of the repository. - Usually this is just its basename. - - -- Function: magit-repolist-column-path - This function inserts the absolute path of the repository. - - -- Function: magit-repolist-column-version - This function inserts a description of the repository’s ‘HEAD’ - revision. - - -- Function: magit-repolist-column-branch - This function inserts the name of the current branch. - - -- Function: magit-repolist-column-upstream - This function inserts the name of the upstream branch of the - current branch. - - -- Function: magit-repolist-column-branches - This function inserts the number of branches. - - -- Function: magit-repolist-column-stashes - This function inserts the number of stashes. - - -- Function: magit-repolist-column-flag - This function inserts a flag as specified by - ‘magit-repolist-column-flag-alist’. - - By default this indicates whether there are uncommitted changes. - - • ‘N’ if there is at least one untracked file. - • ‘U’ if there is at least one unstaged file. - • ‘S’ if there is at least one staged file. - - Only the first one of these that applies is shown. - - -- Function: magit-repolist-column-flags - This functions insert all flags as specified by - ‘magit-repolist-column-flag-alist’. - - This is an alternative to function ‘magit-repolist-column-flag’, - which only lists the first one found. - - -- Function: magit-repolist-column-unpulled-from-upstream - This function inserts the number of upstream commits not in the - current branch. - - -- Function: magit-repolist-column-unpulled-from-pushremote - This function inserts the number of commits in the push branch but - not the current branch. - - -- Function: magit-repolist-column-unpushed-to-upstream - This function inserts the number of commits in the current branch - but not its upstream. - - -- Function: magit-repolist-column-unpushed-to-pushremote - This function inserts the number of commits in the current branch - but not its push branch. - -The following commands are available in repolist buffers: - -‘<RET>’ (‘magit-repolist-status’) - This command shows the status for the repository at point. - -‘m’ (‘magit-repolist-mark’) - This command marks the repository at point. - -‘u’ (‘magit-repolist-unmark’) - This command unmarks the repository at point. - -‘f’ (‘magit-repolist-fetch’) - This command fetches all marked repositories. If no repositories - are marked, then it offers to fetch all displayed repositories. - -‘5’ (‘magit-repolist-find-file-other-frame’) - This command reads a relative file-name (without completion) and - opens the respective file in each marked repository in a new frame. - If no repositories are marked, then it offers to do this for all - displayed repositories. - - -File: magit.info, Node: Logging, Next: Diffing, Prev: Repository List, Up: Inspecting - -5.3 Logging -=========== - -The status buffer contains logs for the unpushed and unpulled commits, -but that obviously isn’t enough. The transient prefix command -‘magit-log’, on ‘l’, features several suffix commands, which show a -specific log in a separate log buffer. - - Like other transient prefix commands, ‘magit-log’ also features -several infix arguments that can be changed before invoking one of the -suffix commands. However, in the case of the log transient, these -arguments may be taken from those currently in use in the current -repository’s log buffer, depending on the value of -‘magit-prefix-use-buffer-arguments’ (see *note Transient Arguments and -Buffer Variables::). - - For information about the various arguments, see *note -(gitman)git-log::. - - The switch ‘++order=VALUE’ is converted to one of -‘--author-date-order’, ‘--date-order’, or ‘--topo-order’ before being -passed to ‘git log’. - - The log transient also features several reflog commands. See *note -Reflog::. - -‘l’ (‘magit-log’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - -‘l l’ (‘magit-log-current’) - Show log for the current branch. When ‘HEAD’ is detached or with a - prefix argument, show log for one or more revs read from the - minibuffer. - -‘l h’ (‘magit-log-head’) - Show log for ‘HEAD’. - -‘l u’ (‘magit-log-related’) - Show log for the current branch, its upstream and its push target. - When the upstream is a local branch, then also show its own - upstream. When ‘HEAD’ is detached, then show log for that, the - previously checked out branch and its upstream and push-target. - -‘l o’ (‘magit-log-other’) - Show log for one or more revs read from the minibuffer. The user - can input any revision or revisions separated by a space, or even - ranges, but only branches, tags, and a representation of the commit - at point are available as completion candidates. - -‘l L’ (‘magit-log-branches’) - Show log for all local branches and ‘HEAD’. - -‘l b’ (‘magit-log-all-branches’) - Show log for all local and remote branches and ‘HEAD’. - -‘l a’ (‘magit-log-all’) - Show log for all references and ‘HEAD’. - - Two additional commands that show the log for the file or blob that -is being visited in the current buffer exists, see *note Commands for -Buffers Visiting Files::. The command ‘magit-cherry’ also shows a log, -see *note Cherries::. - -* Menu: - -* Refreshing Logs:: -* Log Buffer:: -* Log Margin:: -* Select from Log:: -* Reflog:: -* Cherries:: - - -File: magit.info, Node: Refreshing Logs, Next: Log Buffer, Up: Logging - -5.3.1 Refreshing Logs ---------------------- - -The transient prefix command ‘magit-log-refresh’, on ‘L’, can be used to -change the log arguments used in the current buffer, without changing -which log is shown. This works in dedicated log buffers, but also in -the status buffer. - -‘L’ (‘magit-log-refresh’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - -‘L g’ (‘magit-log-refresh’) - This suffix command sets the local log arguments for the current - buffer. - -‘L s’ (‘magit-log-set-default-arguments’) - This suffix command sets the default log arguments for buffers of - the same type as that of the current buffer. Other existing - buffers of the same type are not affected because their local - values have already been initialized. - -‘L w’ (‘magit-log-save-default-arguments’) - This suffix command sets the default log arguments for buffers of - the same type as that of the current buffer, and saves the value - for future sessions. Other existing buffers of the same type are - not affected because their local values have already been - initialized. - -‘L L’ (‘magit-toggle-margin’) - Show or hide the margin. - - -File: magit.info, Node: Log Buffer, Next: Log Margin, Prev: Refreshing Logs, Up: Logging - -5.3.2 Log Buffer ----------------- - -‘L’ (‘magit-log-refresh’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - - See *note Refreshing Logs::. - -‘q’ (‘magit-log-bury-buffer’) - Bury the current buffer or the revision buffer in the same frame. - Like ‘magit-mode-bury-buffer’ (which see) but with a negative - prefix argument instead bury the revision buffer, provided it is - displayed in the current frame. - -‘C-c C-b’ (‘magit-go-backward’) - Move backward in current buffer’s history. - -‘C-c C-f’ (‘magit-go-forward’) - Move forward in current buffer’s history. - -‘C-c C-n’ (‘magit-log-move-to-parent’) - Move to a parent of the current commit. By default, this is the - first parent, but a numeric prefix can be used to specify another - parent. - -‘j’ (‘magit-log-move-to-revision’) - Read a revision and move to it in current log buffer. - - If the chosen reference or revision isn’t being displayed in the - current log buffer, then inform the user about that and do nothing - else. - - If invoked outside any log buffer, then display the log buffer of - the current repository first; creating it if necessary. - -‘<SPC>’ (‘magit-diff-show-or-scroll-up’) - Update the commit or diff buffer for the thing at point. - - Either show the commit or stash at point in the appropriate buffer, - or if that buffer is already being displayed in the current frame - and contains information about that commit or stash, then instead - scroll the buffer up. If there is no commit or stash at point, - then prompt for a commit. - -‘<DEL>’ (‘magit-diff-show-or-scroll-down’) - Update the commit or diff buffer for the thing at point. - - Either show the commit or stash at point in the appropriate buffer, - or if that buffer is already being displayed in the current frame - and contains information about that commit or stash, then instead - scroll the buffer down. If there is no commit or stash at point, - then prompt for a commit. - -‘=’ (‘magit-log-toggle-commit-limit’) - Toggle the number of commits the current log buffer is limited to. - If the number of commits is currently limited, then remove that - limit. Otherwise set it to 256. - -‘+’ (‘magit-log-double-commit-limit’) - Double the number of commits the current log buffer is limited to. - -‘-’ (‘magit-log-half-commit-limit’) - Half the number of commits the current log buffer is limited to. - - -- User Option: magit-log-auto-more - Insert more log entries automatically when moving past the last - entry. Only considered when moving past the last entry with - ‘magit-goto-*-section’ commands. - - -- User Option: magit-log-show-refname-after-summary - Whether to show the refnames after the commit summaries. This is - useful if you use really long branch names. - - -- User Option: magit-log-show-color-graph-limit - When showing more commits than specified by this option, then the - ‘--color’ argument, if specified, is silently dropped. This is - necessary because the ‘ansi-color’ library, which is used to turn - control sequences into faces, is just too slow. - - -- User Option: magit-log-show-signatures-limit - When showing more commits than specified by this option, then the - ‘--show-signature’ argument, if specified, is silently dropped. - This is necessary because checking the signature of a large number - of commits is just too slow. - - Magit displays references in logs a bit differently from how Git does -it. - - Local branches are blue and remote branches are green. Of course -that depends on the used theme, as do the colors used for other types of -references. The current branch has a box around it, as do remote -branches that are their respective remote’s ‘HEAD’ branch. - - If a local branch and its push-target point at the same commit, then -their names are combined to preserve space and to make that relationship -visible. For example: - - origin/feature - [green][blue-] - - instead of - - feature origin/feature - [blue-] [green-------] - - Also note that while the transient features the ‘--show-signature’ -argument, that won’t actually be used when enabled, because Magit -defaults to use just one line per commit. Instead the commit colorized -to indicate the validity of the signed commit object, using the faces -named ‘magit-signature-*’ (which see). - - For a description of ‘magit-log-margin’ see *note Log Margin::. - - -File: magit.info, Node: Log Margin, Next: Select from Log, Prev: Log Buffer, Up: Logging - -5.3.3 Log Margin ----------------- - -In buffers which show one or more logs, it is possible to show -additional information about each commit in the margin. The options -used to configure the margin are named ‘magit-INFIX-margin’, where INFIX -is the same as in the respective major-mode ‘magit-INFIX-mode’. In -regular log buffers that would be ‘magit-log-margin’. - - -- User Option: magit-log-margin - This option specifies whether the margin is initially shown in - Magit-Log mode buffers and how it is formatted. - - The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. - - • If INIT is non-nil, then the margin is shown initially. - • STYLE controls how to format the author or committer date. It - can be one of ‘age’ (to show the age of the commit), - ‘age-abbreviated’ (to abbreviate the time unit to a - character), or a string (suitable for ‘format-time-string’) to - show the actual date. Option - ‘magit-log-margin-show-committer-date’ controls which date is - being displayed. - • WIDTH controls the width of the margin. This exists for - forward compatibility and currently the value should not be - changed. - • AUTHOR controls whether the name of the author is also shown - by default. - • AUTHOR-WIDTH has to be an integer. When the name of the - author is shown, then this specifies how much space is used to - do so. - - You can change the STYLE and AUTHOR-WIDTH of all ‘magit-INFIX-margin’ -options to the same values by customizing ‘magit-log-margin’ *before* -‘magit’ is loaded. If you do that, then the respective values for the -other options will default to what you have set for that variable. -Likewise if you set INIT in ‘magit-log-margin’ to ‘nil’, then that is -used in the default of all other options. But setting it to ‘t’, i.e. -re-enforcing the default for that option, does not carry to other -options. - - -- User Option: magit-log-margin-show-committer-date - This option specifies whether to show the committer date in the - margin. This option only controls whether the committer date is - displayed instead of the author date. Whether some date is - displayed in the margin and whether the margin is displayed at all - is controlled by other options. - -‘L’ (‘magit-margin-settings’) - This transient prefix command binds the following suffix commands, - each of which changes the appearance of the margin in some way. - - In some buffers that support the margin, ‘L’ is instead bound to -‘magit-log-refresh’, but that transient features the same commands, and -then some other unrelated commands. - -‘L L’ (‘magit-toggle-margin’) - This command shows or hides the margin. - -‘L l’ (‘magit-cycle-margin-style’) - This command cycles the style used for the margin. - -‘L d’ (‘magit-toggle-margin-details’) - This command shows or hides details in the margin. - - -File: magit.info, Node: Select from Log, Next: Reflog, Prev: Log Margin, Up: Logging - -5.3.4 Select from Log ---------------------- - -When the user has to select a recent commit that is reachable from -‘HEAD’, using regular completion would be inconvenient (because most -humans cannot remember hashes or "HEAD~5", at least not without double -checking). Instead a log buffer is used to select the commit, which has -the advantage that commits are presented in order and with the commit -message. - - Such selection logs are used when selecting the beginning of a rebase -and when selecting the commit to be squashed into. - - In addition to the key bindings available in all log buffers, the -following additional key bindings are available in selection log -buffers: - -‘C-c C-c’ (‘magit-log-select-pick’) - Select the commit at point and act on it. Call - ‘magit-log-select-pick-function’ with the selected commit as - argument. - -‘C-c C-k’ (‘magit-log-select-quit’) - Abort selecting a commit, don’t act on any commit. - - -- User Option: magit-log-select-margin - This option specifies whether the margin is initially shown in - Magit-Log-Select mode buffers and how it is formatted. - - The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. - - • If INIT is non-nil, then the margin is shown initially. - • STYLE controls how to format the author or committer date. It - can be one of ‘age’ (to show the age of the commit), - ‘age-abbreviated’ (to abbreviate the time unit to a - character), or a string (suitable for ‘format-time-string’) to - show the actual date. Option - ‘magit-log-margin-show-committer-date’ controls which date is - being displayed. - • WIDTH controls the width of the margin. This exists for - forward compatibility and currently the value should not be - changed. - • AUTHOR controls whether the name of the author is also shown - by default. - • AUTHOR-WIDTH has to be an integer. When the name of the - author is shown, then this specifies how much space is used to - do so. - - -File: magit.info, Node: Reflog, Next: Cherries, Prev: Select from Log, Up: Logging - -5.3.5 Reflog ------------- - -Also see *note (gitman)git-reflog::. - - These reflog commands are available from the log transient. See -*note Logging::. - -‘l r’ (‘magit-reflog-current’) - Display the reflog of the current branch. - -‘l O’ (‘magit-reflog-other’) - Display the reflog of a branch or another ref. - -‘l H’ (‘magit-reflog-head’) - Display the ‘HEAD’ reflog. - - -- User Option: magit-reflog-margin - This option specifies whether the margin is initially shown in - Magit-Reflog mode buffers and how it is formatted. - - The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. - - • If INIT is non-nil, then the margin is shown initially. - • STYLE controls how to format the author or committer date. It - can be one of ‘age’ (to show the age of the commit), - ‘age-abbreviated’ (to abbreviate the time unit to a - character), or a string (suitable for ‘format-time-string’) to - show the actual date. Option - ‘magit-log-margin-show-committer-date’ controls which date is - being displayed. - • WIDTH controls the width of the margin. This exists for - forward compatibility and currently the value should not be - changed. - • AUTHOR controls whether the name of the author is also shown - by default. - • AUTHOR-WIDTH has to be an integer. When the name of the - author is shown, then this specifies how much space is used to - do so. - - -File: magit.info, Node: Cherries, Prev: Reflog, Up: Logging - -5.3.6 Cherries --------------- - -Cherries are commits that haven’t been applied upstream (yet), and are -usually visualized using a log. Each commit is prefixed with ‘-’ if it -has an equivalent in the upstream and ‘+’ if it does not, i.e., if it is -a cherry. - - The command ‘magit-cherry’ shows cherries for a single branch, but -the references buffer (see *note References Buffer::) can show cherries -for multiple "upstreams" at once. - - Also see *note (gitman)git-reflog::. - -‘Y’ (‘magit-cherry’) - Show commits that are in a certain branch but that have not been - merged in the upstream branch. - - -- User Option: magit-cherry-margin - This option specifies whether the margin is initially shown in - Magit-Cherry mode buffers and how it is formatted. - - The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. - - • If INIT is non-nil, then the margin is shown initially. - • STYLE controls how to format the author or committer date. It - can be one of ‘age’ (to show the age of the commit), - ‘age-abbreviated’ (to abbreviate the time unit to a - character), or a string (suitable for ‘format-time-string’) to - show the actual date. Option - ‘magit-log-margin-show-committer-date’ controls which date is - being displayed. - • WIDTH controls the width of the margin. This exists for - forward compatibility and currently the value should not be - changed. - • AUTHOR controls whether the name of the author is also shown - by default. - • AUTHOR-WIDTH has to be an integer. When the name of the - author is shown, then this specifies how much space is used to - do so. - - -File: magit.info, Node: Diffing, Next: Ediffing, Prev: Logging, Up: Inspecting - -5.4 Diffing -=========== - -The status buffer contains diffs for the staged and unstaged commits, -but that obviously isn’t enough. The transient prefix command -‘magit-diff’, on ‘d’, features several suffix commands, which show a -specific diff in a separate diff buffer. - - Like other transient prefix commands, ‘magit-diff’ also features -several infix arguments that can be changed before invoking one of the -suffix commands. However, in the case of the diff transient, these -arguments may be taken from those currently in use in the current -repository’s diff buffer, depending on the value of -‘magit-prefix-use-buffer-arguments’ (see *note Transient Arguments and -Buffer Variables::). - - Also see *note (gitman)git-diff::. - -‘d’ (‘magit-diff’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - -‘d d’ (‘magit-diff-dwim’) - Show changes for the thing at point. - -‘d r’ (‘magit-diff-range’) - Show differences between two commits. - - RANGE should be a range (A..B or A...B) but can also be a single - commit. If one side of the range is omitted, then it defaults to - ‘HEAD’. If just a commit is given, then changes in the working - tree relative to that commit are shown. - - If the region is active, use the revisions on the first and last - line of the region. With a prefix argument, instead of diffing the - revisions, choose a revision to view changes along, starting at the - common ancestor of both revisions (i.e., use a "..." range). - -‘d w’ (‘magit-diff-working-tree’) - Show changes between the current working tree and the ‘HEAD’ - commit. With a prefix argument show changes between the working - tree and a commit read from the minibuffer. - -‘d s’ (‘magit-diff-staged’) - Show changes between the index and the ‘HEAD’ commit. With a - prefix argument show changes between the index and a commit read - from the minibuffer. - -‘d u’ (‘magit-diff-unstaged’) - Show changes between the working tree and the index. - -‘d p’ (‘magit-diff-paths’) - Show changes between any two files on disk. - - All of the above suffix commands update the repository’s diff buffer. -The diff transient also features two commands which show differences in -another buffer: - -‘d c’ (‘magit-show-commit’) - Show the commit at point. If there is no commit at point or with a - prefix argument, prompt for a commit. - -‘d t’ (‘magit-stash-show’) - Show all diffs of a stash in a buffer. - - Two additional commands that show the diff for the file or blob that -is being visited in the current buffer exists, see *note Commands for -Buffers Visiting Files::. - -* Menu: - -* Refreshing Diffs:: -* Commands Available in Diffs:: -* Diff Options:: -* Revision Buffer:: - - -File: magit.info, Node: Refreshing Diffs, Next: Commands Available in Diffs, Up: Diffing - -5.4.1 Refreshing Diffs ----------------------- - -The transient prefix command ‘magit-diff-refresh’, on ‘D’, can be used -to change the diff arguments used in the current buffer, without -changing which diff is shown. This works in dedicated diff buffers, but -also in the status buffer. - - (There is one exception; diff arguments cannot be changed in buffers -created by ‘magit-merge-preview’ because the underlying Git command does -not support these arguments.) - -‘D’ (‘magit-diff-refresh’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - -‘D g’ (‘magit-diff-refresh’) - This suffix command sets the local diff arguments for the current - buffer. - -‘D s’ (‘magit-diff-set-default-arguments’) - This suffix command sets the default diff arguments for buffers of - the same type as that of the current buffer. Other existing - buffers of the same type are not affected because their local - values have already been initialized. - -‘D w’ (‘magit-diff-save-default-arguments’) - This suffix command sets the default diff arguments for buffers of - the same type as that of the current buffer, and saves the value - for future sessions. Other existing buffers of the same type are - not affected because their local values have already been - initialized. - -‘D t’ (‘magit-diff-toggle-refine-hunk’) - This command toggles hunk refinement on or off. - -‘D r’ (‘magit-diff-switch-range-type’) - This command converts the diff range type from "revA..revB" to - "revB...revA", or vice versa. - -‘D f’ (‘magit-diff-flip-revs’) - This command swaps revisions in the diff range from "revA..revB" to - "revB..revA", or vice versa. - -‘D F’ (‘magit-diff-toggle-file-filter’) - This command toggles the file restriction of the diffs in the - current buffer, allowing you to quickly switch between viewing all - the changes in the commit and the restricted subset. As a special - case, when this command is called from a log buffer, it toggles the - file restriction in the repository’s revision buffer, which is - useful when you display a revision from a log buffer that is - restricted to a file or files. - - In addition to the above transient, which allows changing any of the -supported arguments, there also exist some commands that change only a -particular argument. - -‘-’ (‘magit-diff-less-context’) - This command decreases the context for diff hunks by COUNT lines. - -‘+’ (‘magit-diff-more-context’) - This command increases the context for diff hunks by COUNT lines. - -‘0’ (‘magit-diff-default-context’) - This command resets the context for diff hunks to the default - height. - - The following commands quickly change what diff is being displayed -without having to using one of the diff transient. - -‘C-c C-d’ (‘magit-diff-while-committing’) - While committing, this command shows the changes that are about to - be committed. While amending, invoking the command again toggles - between showing just the new changes or all the changes that will - be committed. - - This binding is available in the diff buffer as well as the commit - message buffer. - -‘C-c C-b’ (‘magit-go-backward’) - This command moves backward in current buffer’s history. - -‘C-c C-f’ (‘magit-go-forward’) - This command moves forward in current buffer’s history. - - -File: magit.info, Node: Commands Available in Diffs, Next: Diff Options, Prev: Refreshing Diffs, Up: Diffing - -5.4.2 Commands Available in Diffs ---------------------------------- - -Some commands are only available if point is inside a diff. - - ‘magit-diff-visit-file’ and related commands visit the appropriate -version of the file that the diff at point is about. Likewise -‘magit-diff-visit-worktree-file’ and related commands visit the worktree -version of the file that the diff at point is about. See *note Visiting -Files and Blobs from a Diff:: for more information and the key bindings. - -‘C-c C-t’ (‘magit-diff-trace-definition’) - This command shows a log for the definition at point. - - -- User Option: magit-log-trace-definition-function - The function specified by this option is used by - ‘magit-log-trace-definition’ to determine the function at point. - For major-modes that have special needs, you could set the local - value using the mode’s hook. - -‘C-c C-e’ (‘magit-diff-edit-hunk-commit’) - From a hunk, this command edits the respective commit and visits - the file. - - First it visits the file being modified by the hunk at the correct - location using ‘magit-diff-visit-file’. This actually visits a - blob. When point is on a diff header, not within an individual - hunk, then this visits the blob the first hunk is about. - - Then it invokes ‘magit-edit-line-commit’, which uses an interactive - rebase to make the commit editable, or if that is not possible - because the commit is not reachable from ‘HEAD’ by checking out - that commit directly. This also causes the actual worktree file to - be visited. - - Neither the blob nor the file buffer are killed when finishing the - rebase. If that is undesirable, then it might be better to use - ‘magit-rebase-edit-commit’ instead of this command. - -‘j’ (‘magit-jump-to-diffstat-or-diff’) - This command jumps to the diffstat or diff. When point is on a - file inside the diffstat section, then jump to the respective diff - section. Otherwise, jump to the diffstat section or a child - thereof. - - The next two commands are not specific to Magit-Diff mode (or and -Magit buffer for that matter), but it might be worth pointing out that -they are available here too. - -‘<SPC>’ (‘scroll-up’) - This command scrolls text upward. - -‘<DEL>’ (‘scroll-down’) - This command scrolls text downward. - - -File: magit.info, Node: Diff Options, Next: Revision Buffer, Prev: Commands Available in Diffs, Up: Diffing - -5.4.3 Diff Options ------------------- - - -- User Option: magit-diff-refine-hunk - Whether to show word-granularity differences within diff hunks. - - • ‘nil’ Never show fine differences. - • ‘t’ Show fine differences for the current diff hunk only. - • ‘all’ Show fine differences for all displayed diff hunks. - - -- User Option: magit-diff-refine-ignore-whitespace - Whether to ignore whitespace changes in word-granularity - differences. - - -- User Option: magit-diff-adjust-tab-width - Whether to adjust the width of tabs in diffs. - - Determining the correct width can be expensive if it requires - opening large and/or many files, so the widths are cached in the - variable ‘magit-diff--tab-width-cache’. Set that to nil to - invalidate the cache. - - • ‘nil’ Never adjust tab width. Use ‘tab-width’s value from the - Magit buffer itself instead. - - • ‘t’ If the corresponding file-visiting buffer exits, then use - ‘tab-width’’s value from that buffer. Doing this is cheap, so - this value is used even if a corresponding cache entry exists. - - • ‘always’ If there is no such buffer, then temporarily visit - the file to determine the value. - - • NUMBER Like ‘always’, but don’t visit files larger than NUMBER - bytes. - - -- User Option: magit-diff-paint-whitespace - Specify where to highlight whitespace errors. - - See ‘magit-diff-highlight-trailing’, - ‘magit-diff-highlight-indentation’. The symbol ‘t’ means in all - diffs, ‘status’ means only in the status buffer, and nil means - nowhere. - - • ‘nil’ Never highlight whitespace errors. - • ‘t’ Highlight whitespace errors everywhere. - • ‘uncommitted’ Only highlight whitespace errors in diffs - showing uncommitted changes. For backward compatibility - ‘status’ is treated as a synonym. - - -- User Option: magit-diff-paint-whitespace-lines - Specify in what kind of lines to highlight whitespace errors. - - • ‘t’ Highlight only in added lines. - • ‘both’ Highlight in added and removed lines. - • ‘all’ Highlight in added, removed and context lines. - - -- User Option: magit-diff-highlight-trailing - Whether to highlight whitespace at the end of a line in diffs. - Used only when ‘magit-diff-paint-whitespace’ is non-nil. - - -- User Option: magit-diff-highlight-indentation - This option controls whether to highlight the indentation in case - it used the "wrong" indentation style. Indentation is only - highlighted if ‘magit-diff-paint-whitespace’ is also non-nil. - - The value is an alist of the form ‘((REGEXP . INDENT)...)’. The - path to the current repository is matched against each element in - reverse order. Therefore if a REGEXP matches, then earlier - elements are not tried. - - If the used INDENT is ‘tabs’, highlight indentation with tabs. If - INDENT is an integer, highlight indentation with at least that many - spaces. Otherwise, highlight neither. - - -- User Option: magit-diff-hide-trailing-cr-characters - Whether to hide ^M characters at the end of a line in diffs. - - -- User Option: magit-diff-highlight-hunk-region-functions - This option specifies the functions used to highlight the - hunk-internal region. - - ‘magit-diff-highlight-hunk-region-dim-outside’ overlays the outside - of the hunk internal selection with a face that causes the added - and removed lines to have the same background color as context - lines. This function should not be removed from the value of this - option. - - ‘magit-diff-highlight-hunk-region-using-overlays’ and - ‘magit-diff-highlight-hunk-region-using-underline’ emphasize the - region by placing delimiting horizontal lines before and after it. - Both of these functions have glitches which cannot be fixed due to - limitations of Emacs’ display engine. For more information see - <https://github.com/magit/magit/issues/2758> ff. - - Instead of, or in addition to, using delimiting horizontal lines, - to emphasize the boundaries, you may wish to emphasize the text - itself, using ‘magit-diff-highlight-hunk-region-using-face’. - - In terminal frames it’s not possible to draw lines as the overlay - and underline variants normally do, so there they fall back to - calling the face function instead. - - -- User Option: magit-diff-unmarked-lines-keep-foreground - This option controls whether added and removed lines outside the - hunk-internal region only lose their distinct background color or - also the foreground color. Whether the outside of the region is - dimmed at all depends on - ‘magit-diff-highlight-hunk-region-functions’. - - -- User Option: magit-diff-extra-stat-arguments - This option specifies additional arguments to be used alongside - ‘--stat’. - - The value is a list of zero or more arguments or a function that - takes no argument and returns such a list. These arguments are - allowed here: ‘--stat-width’, ‘--stat-name-width’, - ‘--stat-graph-width’ and ‘--compact-summary’. Also see *note - (gitman)git-diff::. - - -File: magit.info, Node: Revision Buffer, Prev: Diff Options, Up: Diffing - -5.4.4 Revision Buffer ---------------------- - - -- User Option: magit-revision-insert-related-refs - Whether to show related branches in revision buffers. - - • ‘nil’ Don’t show any related branches. - • ‘t’ Show related local branches. - • ‘all’ Show related local and remote branches. - • ‘mixed’ Show all containing branches and local merged - branches. - - -- User Option: magit-revision-show-gravatars - Whether to show gravatar images in revision buffers. - - If ‘nil’, then don’t insert any gravatar images. If ‘t’, then - insert both images. If ‘author’ or ‘committer’, then insert only - the respective image. - - If you have customized the option ‘magit-revision-headers-format’ - and want to insert the images then you might also have to specify - where to do so. In that case the value has to be a cons-cell of - two regular expressions. The car specifies where to insert the - author’s image. The top half of the image is inserted right after - the matched text, the bottom half on the next line in the same - column. The cdr specifies where to insert the committer’s image, - accordingly. Either the car or the cdr may be nil." - - -- User Option: magit-revision-use-hash-sections - Whether to turn hashes inside the commit message into sections. - - If non-nil, then hashes inside the commit message are turned into - ‘commit’ sections. There is a trade off to be made between - performance and reliability: - - • ‘slow’ calls git for every word to be absolutely sure. - • ‘quick’ skips words less than seven characters long. - • ‘quicker’ additionally skips words that don’t contain a - number. - • ‘quickest’ uses all words that are at least seven characters - long and which contain at least one number as well as at least - one letter. - - If nil, then no hashes are turned into sections, but you can still - visit the commit at point using "RET". - - The diffs shown in the revision buffer may be automatically -restricted to a subset of the changed files. If the revision buffer is -displayed from a log buffer, the revision buffer will share the same -file restriction as that log buffer (also see the command -‘magit-diff-toggle-file-filter’). - - -- User Option: magit-revision-filter-files-on-follow - Whether showing a commit from a log buffer honors the log’s file - filter when the log arguments include ‘--follow’. - - When this option is nil, displaying a commit from a log ignores the - log’s file filter if the log arguments include ‘--follow’. Doing - so avoids showing an empty diff in revision buffers for commits - before a rename event. In such cases, the ‘--patch’ argument of - the log transient can be used to show the file-restricted diffs - inline. - - Set this option to non-nil to keep the log’s file restriction even - if ‘--follow’ is present in the log arguments. - - If the revision buffer is not displayed from a log buffer, the file -restriction is determined as usual (see *note Transient Arguments and -Buffer Variables::). - - -File: magit.info, Node: Ediffing, Next: References Buffer, Prev: Diffing, Up: Inspecting - -5.5 Ediffing -============ - -This section describes how to enter Ediff from Magit buffers. For -information on how to use Ediff itself, see *note (ediff)Top::. - -‘e’ (‘magit-ediff-dwim’) - Compare, stage, or resolve using Ediff. - - This command tries to guess what file, and what commit or range the - user wants to compare, stage, or resolve using Ediff. It might - only be able to guess either the file, or range/commit, in which - case the user is asked about the other. It might not always guess - right, in which case the appropriate ‘magit-ediff-*’ command has to - be used explicitly. If it cannot read the user’s mind at all, then - it asks the user for a command to run. - -‘E’ (‘magit-ediff’) - This transient prefix command binds the following suffix commands - and displays them in a temporary buffer until a suffix is invoked. - -‘E r’ (‘magit-ediff-compare’) - Compare two revisions of a file using Ediff. - - If the region is active, use the revisions on the first and last - line of the region. With a prefix argument, instead of diffing the - revisions, choose a revision to view changes along, starting at the - common ancestor of both revisions (i.e., use a "..." range). - -‘E m’ (‘magit-ediff-resolve-rest’) - This command allows you to resolve outstanding conflicts in the - file at point using Ediff. If there is no file at point or if it - doesn’t have any unmerged changes, then this command prompts for a - file. - - Provided that the value of ‘merge.conflictstyle’ is ‘diff3’, you - can view the file’s merge-base revision using ‘/’ in the Ediff - control buffer. - - The A, B and Ancestor buffers are constructed from the conflict - markers in the worktree file. Because you and/or Git may have - already resolved some conflicts, that means that these buffers may - not contain the actual versions from the respective blobs. - -‘E M’ (‘magit-ediff-resolve-all’) - This command allows you to resolve all conflicts in the file at - point using Ediff. If there is no file at point or if it doesn’t - have any unmerged changes, then this command prompts for a file. - - Provided that the value of ‘merge.conflictstyle’ is ‘diff3’, you - can view the file’s merge-base revision using ‘/’ in the Ediff - control buffer. - - First the file in the worktree is moved aside, appending the suffix - ‘.ORIG’, so that you could later go back to that version. Then it - is reconstructed from the two sides of the conflict and the - merge-base, if available. - - It would be nice if the worktree file were just used as-is, but - Ediff does not support that. This means that all conflicts, that - Git has already resolved, are restored. On the other hand Ediff - also tries to resolve conflicts, and in many cases Ediff and Git - should produce similar results. - - However if you have already resolved some conflicts manually, then - those changes are discarded (though you can recover them from the - backup file). In such cases ‘magit-ediff-resolve-rest’ might be - more suitable. - - The advantage that this command has over ‘magit-ediff-resolve-rest’ - is that the A, B and Ancestor buffers correspond to blobs from the - respective commits, allowing you to inspect a side in context and - to use Magit commands in these buffers to do so. Blame and log - commands are particularly useful here. - -‘E t’ (‘magit-git-mergetool’) - This command does not actually use Ediff. While it serves the same - purpose as ‘magit-ediff-resolve-rest’, it uses ‘git mergetool - --gui’ to resolve conflicts. - - With a prefix argument this acts as a transient prefix command, - allowing the user to select the mergetool and change some settings. - -‘E s’ (‘magit-ediff-stage’) - Stage and unstage changes to a file using Ediff, defaulting to the - file at point. - -‘E u’ (‘magit-ediff-show-unstaged’) - Show unstaged changes to a file using Ediff. - -‘E i’ (‘magit-ediff-show-staged’) - Show staged changes to a file using Ediff. - -‘E w’ (‘magit-ediff-show-working-tree’) - Show changes in a file between ‘HEAD’ and working tree using Ediff. - -‘E c’ (‘magit-ediff-show-commit’) - Show changes to a file introduced by a commit using Ediff. - -‘E z’ (‘magit-ediff-show-stash’) - Show changes to a file introduced by a stash using Ediff. - - -- User Option: magit-ediff-dwim-resolve-function - This option controls which function ‘magit-ediff-dwim’ uses to - resolve conflicts. One of ‘magit-ediff-resolve-rest’, - ‘magit-ediff-resolve-all’ or ‘magit-git-mergetool’; which are all - discussed above. - - -- User Option: magit-ediff-dwim-show-on-hunks - This option controls what command ‘magit-ediff-dwim’ calls when - point is on uncommitted hunks. When nil, always run - ‘magit-ediff-stage’. Otherwise, use ‘magit-ediff-show-staged’ and - ‘magit-ediff-show-unstaged’ to show staged and unstaged changes, - respectively. - - -- User Option: magit-ediff-show-stash-with-index - This option controls whether ‘magit-ediff-show-stash’ includes a - buffer containing the file’s state in the index at the time the - stash was created. This makes it possible to tell which changes in - the stash were staged. - - -- User Option: magit-ediff-quit-hook - This hook is run after quitting an Ediff session that was created - using a Magit command. The hook functions are run inside the Ediff - control buffer, and should not change the current buffer. - - This is similar to ‘ediff-quit-hook’ but takes the needs of Magit - into account. The regular ‘ediff-quit-hook’ is ignored by Ediff - sessions that were created using a Magit command. - - -File: magit.info, Node: References Buffer, Next: Bisecting, Prev: Ediffing, Up: Inspecting - -5.6 References Buffer -===================== - -‘y’ (‘magit-show-refs’) - This command lists branches and tags in a dedicated buffer. - - However if this command is invoked again from this buffer or if it - is invoked with a prefix argument, then it acts as a transient - prefix command, which binds the following suffix commands and some - infix arguments. - - All of the following suffix commands list exactly the same branches -and tags. The only difference the optional feature that can be enabled -by changing the value of ‘magit-refs-show-commit-count’ (see below). -These commands specify a different branch or commit against which all -the other references are compared. - -‘y y’ (‘magit-show-refs-head’) - This command lists branches and tags in a dedicated buffer. Each - reference is being compared with ‘HEAD’. - -‘y c’ (‘magit-show-refs-current’) - This command lists branches and tags in a dedicated buffer. Each - reference is being compared with the current branch or ‘HEAD’ if it - is detached. - -‘y o’ (‘magit-show-refs-other’) - This command lists branches and tags in a dedicated buffer. Each - reference is being compared with a branch read from the user. - -‘y r’ (‘magit-refs-set-show-commit-count’) - This command changes for which refs the commit count is shown. - - -- User Option: magit-refs-show-commit-count - Whether to show commit counts in Magit-Refs mode buffers. - - • ‘all’ Show counts for branches and tags. - • ‘branch’ Show counts for branches only. - • ‘nil’ Never show counts. - - The default is ‘nil’ because anything else can be very expensive. - - -- User Option: magit-refs-pad-commit-counts - Whether to pad all commit counts on all sides in Magit-Refs mode - buffers. - - If this is nil, then some commit counts are displayed right next to - one of the branches that appear next to the count, without any - space in between. This might look bad if the branch name faces - look too similar to ‘magit-dimmed’. - - If this is non-nil, then spaces are placed on both sides of all - commit counts. - - -- User Option: magit-refs-show-remote-prefix - Whether to show the remote prefix in lists of remote branches. - - Showing the prefix is redundant because the name of the remote is - already shown in the heading preceding the list of its branches. - - -- User Option: magit-refs-primary-column-width - Width of the primary column in ‘magit-refs-mode’ buffers. The - primary column is the column that contains the name of the branch - that the current row is about. - - If this is an integer, then the column is that many columns wide. - Otherwise it has to be a cons-cell of two integers. The first - specifies the minimal width, the second the maximal width. In that - case the actual width is determined using the length of the names - of the shown local branches. (Remote branches and tags are not - taken into account when calculating to optimal width.) - - -- User Option: magit-refs-focus-column-width - Width of the focus column in ‘magit-refs-mode’ buffers. - - The focus column is the first column, which marks one branch - (usually the current branch) as the focused branch using ‘*’ or - ‘@’. For each other reference, this column optionally shows how - many commits it is ahead of the focused branch and ‘<’, or if it - isn’t ahead then the commits it is behind and ‘>’, or if it isn’t - behind either, then a ‘=’. - - This column may also display only ‘*’ or ‘@’ for the focused - branch, in which case this option is ignored. Use ‘L v’ to change - the verbosity of this column. - - -- User Option: magit-refs-margin - This option specifies whether the margin is initially shown in - Magit-Refs mode buffers and how it is formatted. - - The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. - - • If INIT is non-nil, then the margin is shown initially. - • STYLE controls how to format the author or committer date. It - can be one of ‘age’ (to show the age of the commit), - ‘age-abbreviated’ (to abbreviate the time unit to a - character), or a string (suitable for ‘format-time-string’) to - show the actual date. Option - ‘magit-log-margin-show-committer-date’ controls which date is - being displayed. - • WIDTH controls the width of the margin. This exists for - forward compatibility and currently the value should not be - changed. - • AUTHOR controls whether the name of the author is also shown - by default. - • AUTHOR-WIDTH has to be an integer. When the name of the - author is shown, then this specifies how much space is used to - do so. - - -- User Option: magit-refs-margin-for-tags - This option specifies whether to show information about tags in the - margin. This is disabled by default because it is slow if there - are many tags. - - The following variables control how individual refs are displayed. -If you change one of these variables (especially the "%c" part), then -you should also change the others to keep things aligned. The following -%-sequences are supported: - - • ‘%a’ Number of commits this ref has over the one we compare to. - • ‘%b’ Number of commits the ref we compare to has over this one. - • ‘%c’ Number of commits this ref has over the one we compare to. - For the ref which all other refs are compared this is instead "@", - if it is the current branch, or "#" otherwise. - • ‘%C’ For the ref which all other refs are compared this is "@", if - it is the current branch, or "#" otherwise. For all other refs " - ". - • ‘%h’ Hash of this ref’s tip. - • ‘%m’ Commit summary of the tip of this ref. - • ‘%n’ Name of this ref. - • ‘%u’ Upstream of this local branch. - • ‘%U’ Upstream of this local branch and additional local vs. - upstream information. - - -- User Option: magit-refs-filter-alist - The purpose of this option is to forgo displaying certain refs - based on their name. If you want to not display any refs of a - certain type, then you should remove the appropriate function from - ‘magit-refs-sections-hook’ instead. - - This alist controls which tags and branches are omitted from being - displayed in ‘magit-refs-mode’ buffers. If it is ‘nil’, then all - refs are displayed (subject to ‘magit-refs-sections-hook’). - - All keys are tried in order until one matches. Then its value is - used and subsequent elements are ignored. If the value is non-nil, - then the reference is displayed, otherwise it is not. If no - element matches, then the reference is displayed. - - A key can either be a regular expression that the refname has to - match, or a function that takes the refname as only argument and - returns a boolean. A remote branch such as "origin/master" is - displayed as just "master", however for this comparison the former - is used. - -‘<RET>’ (‘magit-visit-ref’) - This command visits the reference or revision at point in another - buffer. If there is no revision at point or with a prefix argument - then it prompts for a revision. - - This command behaves just like ‘magit-show-commit’ as described - above, except if point is on a reference in a ‘magit-refs-mode’ - buffer, in which case the behavior may be different, but only if - you have customized the option ‘magit-visit-ref-behavior’. - - -- User Option: magit-visit-ref-behavior - This option controls how ‘magit-visit-ref’ behaves in - ‘magit-refs-mode’ buffers. - - By default ‘magit-visit-ref’ behaves like ‘magit-show-commit’, in - all buffers, including ‘magit-refs-mode’ buffers. When the type of - the section at point is ‘commit’ then "RET" is bound to - ‘magit-show-commit’, and when the type is either ‘branch’ or ‘tag’ - then it is bound to ‘magit-visit-ref’. - - "RET" is one of Magit’s most essential keys and at least by default - it should behave consistently across all of Magit, especially - because users quickly learn that it does something very harmless; - it shows more information about the thing at point in another - buffer. - - However "RET" used to behave differently in ‘magit-refs-mode’ - buffers, doing surprising things, some of which cannot really be - described as "visit this thing". If you’ve grown accustomed this - behavior, you can restore it by adding one or more of the below - symbols to the value of this option. But keep in mind that by - doing so you don’t only introduce inconsistencies, you also lose - some functionality and might have to resort to ‘M-x - magit-show-commit’ to get it back. - - ‘magit-visit-ref’ looks for these symbols in the order in which - they are described here. If the presence of a symbol applies to - the current situation, then the symbols that follow do not affect - the outcome. - - • ‘focus-on-ref’ - - With a prefix argument update the buffer to show commit counts - and lists of cherry commits relative to the reference at point - instead of relative to the current buffer or ‘HEAD’. - - Instead of adding this symbol, consider pressing "C-u y o - RET". - - • ‘create-branch’ - - If point is on a remote branch, then create a new local branch - with the same name, use the remote branch as its upstream, and - then check out the local branch. - - Instead of adding this symbol, consider pressing "b c RET - RET", like you would do in other buffers. - - • ‘checkout-any’ - - Check out the reference at point. If that reference is a tag - or a remote branch, then this results in a detached ‘HEAD’. - - Instead of adding this symbol, consider pressing "b b RET", - like you would do in other buffers. - - • ‘checkout-branch’ - - Check out the local branch at point. - - Instead of adding this symbol, consider pressing "b b RET", - like you would do in other buffers. - -* Menu: - -* References Sections:: - - -File: magit.info, Node: References Sections, Up: References Buffer - -5.6.1 References Sections -------------------------- - -The contents of references buffers is controlled using the hook -‘magit-refs-sections-hook’. See *note Section Hooks:: to learn about -such hooks and how to customize them. All of the below functions are -members of the default value. Note that it makes much less sense to -customize this hook than it does for the respective hook used for the -status buffer. - - -- User Option: magit-refs-sections-hook - Hook run to insert sections into a references buffer. - - -- Function: magit-insert-local-branches - Insert sections showing all local branches. - - -- Function: magit-insert-remote-branches - Insert sections showing all remote-tracking branches. - - -- Function: magit-insert-tags - Insert sections showing all tags. - - -File: magit.info, Node: Bisecting, Next: Visiting Files and Blobs, Prev: References Buffer, Up: Inspecting - -5.7 Bisecting -============= - -Also see *note (gitman)git-bisect::. - -‘B’ (‘magit-bisect’) - This transient prefix command binds the following suffix commands - and displays them in a temporary buffer until a suffix is invoked. - - When bisecting is not in progress, then the transient features the -following suffix commands. - -‘B B’ (‘magit-bisect-start’) - Start a bisect session. - - Bisecting a bug means to find the commit that introduced it. This - command starts such a bisect session by asking for a known good - commit and a known bad commit. If you’re bisecting a change that - isn’t a regression, you can select alternate terms that are - conceptually more fitting than "bad" and "good", but the infix - arguments to do so are disabled by default. - -‘B s’ (‘magit-bisect-run’) - Bisect automatically by running commands after each step. - - When bisecting in progress, then the transient instead features the -following suffix commands. - -‘B b’ (‘magit-bisect-bad’) - Mark the current commit as bad. Use this after you have asserted - that the commit does contain the bug in question. - -‘B g’ (‘magit-bisect-good’) - Mark the current commit as good. Use this after you have asserted - that the commit does not contain the bug in question. - -‘B m’ (‘magit-bisect-mark’) - Mark the current commit with one of the bisect terms. This command - provides an alternative to ‘magit-bisect-bad’ and - ‘magit-bisect-good’ and is useful when using terms other than "bad" - and "good". This suffix is disabled by default. - -‘B k’ (‘magit-bisect-skip’) - Skip the current commit. Use this if for some reason the current - commit is not a good one to test. This command lets Git choose a - different one. - -‘B r’ (‘magit-bisect-reset’) - After bisecting, cleanup bisection state and return to original - ‘HEAD’. - - By default the status buffer shows information about the ongoing -bisect session. - - -- User Option: magit-bisect-show-graph - This option controls whether a graph is displayed for the log of - commits that still have to be bisected. - - -File: magit.info, Node: Visiting Files and Blobs, Next: Blaming, Prev: Bisecting, Up: Inspecting - -5.8 Visiting Files and Blobs -============================ - -Magit provides several commands that visit a file or blob (the version -of a file that is stored in a certain commit). Actually it provides -several *groups* of such commands and the several *variants* within each -group. - - Also see *note Commands for Buffers Visiting Files::. - -* Menu: - -* General-Purpose Visit Commands:: -* Visiting Files and Blobs from a Diff:: - - -File: magit.info, Node: General-Purpose Visit Commands, Next: Visiting Files and Blobs from a Diff, Up: Visiting Files and Blobs - -5.8.1 General-Purpose Visit Commands ------------------------------------- - -These commands can be used anywhere to open any blob. Currently no keys -are bound to these commands by default, but that is likely to change. - - -- Command: magit-find-file - This command reads a filename and revision from the user and visits - the respective blob in a buffer. The buffer is displayed in the - selected window. - - -- Command: magit-find-file-other-window - This command reads a filename and revision from the user and visits - the respective blob in a buffer. The buffer is displayed in - another window. - - -- Command: magit-find-file-other-frame - This command reads a filename and revision from the user and visits - the respective blob in a buffer. The buffer is displayed in - another frame. - - -File: magit.info, Node: Visiting Files and Blobs from a Diff, Prev: General-Purpose Visit Commands, Up: Visiting Files and Blobs - -5.8.2 Visiting Files and Blobs from a Diff ------------------------------------------- - -These commands can only be used when point is inside a diff. - -‘<RET>’ (‘magit-diff-visit-file’) - This command visits the appropriate version of the file that the - diff at point is about. - - This commands visits the worktree version of the appropriate file. - The location of point inside the diff determines which file is - being visited. The visited version depends on what changes the - diff is about. - - 1. If the diff shows uncommitted changes (i.e., staged or - unstaged changes), then visit the file in the working tree - (i.e., the same "real" file that ‘find-file’ would visit. In - all other cases visit a "blob" (i.e., the version of a file as - stored in some commit). - - 2. If point is on a removed line, then visit the blob for the - first parent of the commit that removed that line, i.e., the - last commit where that line still exists. - - 3. If point is on an added or context line, then visit the blob - that adds that line, or if the diff shows from more than a - single commit, then visit the blob from the last of these - commits. - - In the file-visiting buffer this command goes to the line that - corresponds to the line that point is on in the diff. - - The buffer is displayed in the selected window. With a prefix - argument the buffer is displayed in another window instead. - - -- User Option: magit-diff-visit-previous-blob - This option controls whether ‘magit-diff-visit-file’ may visit the - previous blob. When this is ‘t’ (the default) and point is on a - removed line in a diff for a committed change, then - ‘magit-diff-visit-file’ visits the blob from the last revision - which still had that line. - - Currently this is only supported for committed changes, for staged - and unstaged changes ‘magit-diff-visit-file’ always visits the file - in the working tree. - -‘C-<return>’ (‘magit-diff-visit-file-worktree’) - This command visits the worktree version of the appropriate file. - The location of point inside the diff determines which file is - being visited. Unlike ‘magit-diff-visit-file’ it always visits the - "real" file in the working tree, i.e the "current version" of the - file. - - In the file-visiting buffer this command goes to the line that - corresponds to the line that point is on in the diff. Lines that - were added or removed in the working tree, the index and other - commits in between are automatically accounted for. - - The buffer is displayed in the selected window. With a prefix - argument the buffer is displayed in another window instead. - - Variants of the above two commands exist that instead visit the file -in another window or in another frame. If you prefer such behavior, -then you may want to change the above key bindings, but note that the -above commands also use another window when invoked with a prefix -argument. - - -- Command: magit-diff-visit-file-other-window - -- Command: magit-diff-visit-file-other-frame - -- Command: magit-diff-visit-worktree-file-other-window - -- Command: magit-diff-visit-worktree-file-other-frame - - -File: magit.info, Node: Blaming, Prev: Visiting Files and Blobs, Up: Inspecting - -5.9 Blaming -=========== - -Also see *note (gitman)git-blame::. - - To start blaming, invoke the ‘magit-file-dispatch’ transient prefix -command. When using the default key bindings, that can be done by -pressing ‘C-c M-g’. When using the recommended bindings, this command -is instead bound to ‘C-c f’. Also see *note Global Bindings::. - - The blaming suffix commands can be invoked directly from the file -dispatch transient. However if you want to set an infix argument, then -you have to enter the blaming sub-prefix first. - -‘C-c f B’ (‘magit-blame’) -‘C-c f b’ (‘magit-blame-addition’) -‘C-c f B b’ -‘C-c f r’ (‘magit-blame-removal’) -‘C-c f B r’ -‘C-c f f’ (‘magit-blame-reverse’) -‘C-c f B f’ -‘C-c f e’ (‘magit-blame-echo’) -‘C-c f B e’ -‘C-c f q’ (‘magit-blame-quit’) -‘C-c f B q’ - Each of these commands is documented individually right below, - alongside their default key bindings. The bindings shown above are - the recommended bindings, which you can enable by following the - instructions in *note Global Bindings::. - -‘C-c M-g B’ (‘magit-blame’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - - Note that not all of the following suffixes are available at all -times. For example if ‘magit-blame-mode’ is not enabled, then the -command whose purpose is to turn off that mode would not be of any use -and therefore isn’t available. - -‘C-c M-g b’ (‘magit-blame-addition’) -‘C-c M-g B b’ - This command augments each line or chunk of lines in the current - file-visiting or blob-visiting buffer with information about what - commits last touched these lines. - - If the buffer visits a revision of that file, then history up to - that revision is considered. Otherwise, the file’s full history is - considered, including uncommitted changes. - - If Magit-Blame mode is already turned on in the current buffer then - blaming is done recursively, by visiting REVISION:FILE (using - ‘magit-find-file’), where REVISION is a parent of the revision that - added the current line or chunk of lines. - -‘C-c M-g r’ (‘magit-blame-removal’) -‘C-c M-g B r’ - This command augments each line or chunk of lines in the current - blob-visiting buffer with information about the revision that - removes it. It cannot be used in file-visiting buffers. - - Like ‘magit-blame-addition’, this command can be used recursively. - -‘C-c M-g f’ (‘magit-blame-reverse’) -‘C-c M-g B f’ - This command augments each line or chunk of lines in the current - file-visiting or blob-visiting buffer with information about the - last revision in which a line still existed. - - Like ‘magit-blame-addition’, this command can be used recursively. - -‘C-c M-g e’ (‘magit-blame-echo’) -‘C-c M-g B e’ - This command is like ‘magit-blame-addition’ except that it doesn’t - turn on ‘read-only-mode’ and that it initially uses the - visualization style specified by option ‘magit-blame-echo-style’. - - The following key bindings are available when Magit-Blame mode is -enabled and Read-Only mode is not enabled. These commands are also -available in other buffers; here only the behavior is described that is -relevant in file-visiting buffers that are being blamed. - -‘C-c M-g q’ (‘magit-blame-quit’) -‘C-c M-g B q’ - This command turns off Magit-Blame mode. If the buffer was created - during a recursive blame, then it also kills the buffer. - -‘<RET>’ (‘magit-show-commit’) - This command shows the commit that last touched the line at point. - -‘<SPC>’ (‘magit-diff-show-or-scroll-up’) - This command updates the commit buffer. - - This either shows the commit that last touched the line at point in - the appropriate buffer, or if that buffer is already being - displayed in the current frame and if that buffer contains - information about that commit, then the buffer is scrolled up - instead. - -‘<DEL>’ (‘magit-diff-show-or-scroll-down’) - This command updates the commit buffer. - - This either shows the commit that last touched the line at point in - the appropriate buffer, or if that buffer is already being - displayed in the current frame and if that buffer contains - information about that commit, then the buffer is scrolled down - instead. - - The following key bindings are available when both Magit-Blame mode -and Read-Only mode are enabled. - -‘b’ (‘magit-blame’) - See above. - -‘n’ (‘magit-blame-next-chunk’) - This command moves to the next chunk. - -‘N’ (‘magit-blame-next-chunk-same-commit’) - This command moves to the next chunk from the same commit. - -‘p’ (‘magit-blame-previous-chunk’) - This command moves to the previous chunk. - -‘P’ (‘magit-blame-previous-chunk-same-commit’) - This command moves to the previous chunk from the same commit. - -‘q’ (‘magit-blame-quit’) - This command turns off Magit-Blame mode. If the buffer was created - during a recursive blame, then it also kills the buffer. - -‘M-w’ (‘magit-blame-copy-hash’) - This command saves the hash of the current chunk’s commit to the - kill ring. - - When the region is active, the command saves the region’s content - instead of the hash, like ‘kill-ring-save’ would. - -‘c’ (‘magit-blame-cycle-style’) - This command changes how blame information is visualized in the - current buffer by cycling through the styles specified using the - option ‘magit-blame-styles’. - - Blaming is also controlled using the following options. - - -- User Option: magit-blame-styles - This option defines a list of styles used to visualize blame - information. For now see its doc-string to learn more. - - -- User Option: magit-blame-echo-style - This option specifies the blame visualization style used by the - command ‘magit-blame-echo’. This must be a symbol that is used as - the identifier for one of the styles defined in - ‘magit-blame-styles’. - - -- User Option: magit-blame-time-format - This option specifies the format string used to display times when - showing blame information. - - -- User Option: magit-blame-read-only - This option controls whether blaming a buffer also makes - temporarily read-only. - - -- User Option: magit-blame-disable-modes - This option lists incompatible minor-modes that should be disabled - temporarily when a buffer contains blame information. They are - enabled again when the buffer no longer shows blame information. - - -- User Option: magit-blame-goto-chunk-hook - This hook is run when moving between chunks. - - -File: magit.info, Node: Manipulating, Next: Transferring, Prev: Inspecting, Up: Top - -6 Manipulating -************** - -* Menu: - -* Creating Repository:: -* Cloning Repository:: -* Staging and Unstaging:: -* Applying:: -* Committing:: -* Branching:: -* Merging:: -* Resolving Conflicts:: -* Rebasing:: -* Cherry Picking:: -* Resetting:: -* Stashing:: - - -File: magit.info, Node: Creating Repository, Next: Cloning Repository, Up: Manipulating - -6.1 Creating Repository -======================= - -‘I’ (‘magit-init’) - This command initializes a repository and then shows the status - buffer for the new repository. - - If the directory is below an existing repository, then the user has - to confirm that a new one should be created inside. If the - directory is the root of the existing repository, then the user has - to confirm that it should be reinitialized. - - -File: magit.info, Node: Cloning Repository, Next: Staging and Unstaging, Prev: Creating Repository, Up: Manipulating - -6.2 Cloning Repository -====================== - -To clone a remote or local repository use ‘C’, which is bound to the -command ‘magit-clone’. This command either act as a transient prefix -command, which binds several infix arguments and suffix commands, or it -can invoke ‘git clone’ directly, depending on whether a prefix argument -is used and on the value of ‘magit-clone-always-transient’. - - -- User Option: magit-clone-always-transient - This option controls whether the command ‘magit-clone’ always acts - as a transient prefix command, regardless of whether a prefix - argument is used or not. If ‘t’, then that command always acts as - a transient prefix. If ‘nil’, then a prefix argument has to be - used for it to act as a transient. - -‘C’ (‘magit-clone’) - This command either acts as a transient prefix command as described - above or does the same thing as ‘transient-clone-regular’ as - described below. - - If it acts as a transient prefix, then it binds the following - suffix commands and several infix arguments. - -‘C C’ (‘magit-clone-regular’) - This command creates a regular clone of an existing repository. - The repository and the target directory are read from the user. - -‘C s’ (‘magit-clone-shallow’) - This command creates a shallow clone of an existing repository. - The repository and the target directory are read from the user. By - default the depth of the cloned history is a single commit, but - with a prefix argument the depth is read from the user. - -‘C >’ (‘magit-clone-sparse’) - This command creates a clone of an existing repository and - initializes a sparse checkout, avoiding a checkout of the full - working tree. To add more directories, use the - ‘magit-sparse-checkout’ transient (see *note Sparse checkouts::). - -‘C b’ (‘magit-clone-bare’) - This command creates a bare clone of an existing repository. The - repository and the target directory are read from the user. - -‘C m’ (‘magit-clone-mirror’) - This command creates a mirror of an existing repository. The - repository and the target directory are read from the user. - - The following suffixes are disabled by default. See *note -(transient)Enabling and Disabling Suffixes:: for how to enable them. - -‘C d’ (‘magit-clone-shallow-since’) - This command creates a shallow clone of an existing repository. - Only commits that were committed after a date are cloned, which is - read from the user. The repository and the target directory are - also read from the user. - -‘C e’ (‘magit-clone-shallow-exclude’) - This command creates a shallow clone of an existing repository. - This reads a branch or tag from the user. Commits that are - reachable from that are not cloned. The repository and the target - directory are also read from the user. - - -- User Option: magit-clone-set-remote-head - This option controls whether cloning causes the reference - ‘refs/remotes/<remote>/HEAD’ to be created in the clone. The - default is to delete the reference after running ‘git clone’, which - insists on creating it. This is because the reference has not been - found to be particularly useful as it is not automatically updated - when the ‘HEAD’ of the remote changes. Setting this option to ‘t’ - preserves Git’s default behavior of creating the reference. - - -- User Option: magit-clone-set-remote.pushDefault - This option controls whether the value of the Git variable - ‘remote.pushDefault’ is set after cloning. - - • If ‘t’, then it is always set without asking. - • If ‘ask’, then the users are asked every time they clone a - repository. - • If ‘nil’, then it is never set. - - -- User Option: magit-clone-default-directory - This option control the default directory name used when reading - the destination for a cloning operation. - - • If ‘nil’ (the default), then the value of ‘default-directory’ - is used. - • If a directory, then that is used. - • If a function, then that is called with the remote url as the - only argument and the returned value is used. - - -- User Option: magit-clone-name-alist - This option maps regular expressions, which match repository names, - to repository urls, making it possible for users to enter short - names instead of urls when cloning repositories. - - Each element has the form ‘(REGEXP HOSTNAME USER)’. When the user - enters a name when a cloning command asks for a name or url, then - that is looked up in this list. The first element whose REGEXP - matches is used. - - The format specified by option ‘magit-clone-url-format’ is used to - turn the name into an url, using HOSTNAME and the repository name. - If the provided name contains a slash, then that is used. - Otherwise if the name omits the owner of the repository, then the - default user specified in the matched entry is used. - - If USER contains a dot, then it is treated as a Git variable and - the value of that is used as the username. Otherwise it is used as - the username itself. - - -- User Option: magit-clone-url-format - The format specified by this option is used when turning repository - names into urls. ‘%h’ is the hostname and ‘%n’ is the repository - name, including the name of the owner. The value can be a string - (representing a single static format) or an alist with elements - ‘(HOSTNAME . FORMAT)’ mapping hostnames to formats. When an alist - is used, the ‘t’ key represents the default format. - - Example of a single format string: - - (setq magit-clone-url-format - "git@%h:%n.git") - - Example of by-hostname format strings: - - (setq magit-clone-url-format - '(("git.example.com" . "git@%h:~%n") - (nil . "git@%h:%n.git"))) - - -- User Option: magit-post-clone-hook - Hook run after the Git process has successfully finished cloning - the repository. When the hook is called, ‘default-directory’ is - let-bound to the directory where the repository has been cloned. - - -File: magit.info, Node: Staging and Unstaging, Next: Applying, Prev: Cloning Repository, Up: Manipulating - -6.3 Staging and Unstaging -========================= - -Like Git, Magit can of course stage and unstage complete files. Unlike -Git, it also allows users to gracefully un-/stage individual hunks and -even just part of a hunk. To stage individual hunks and parts of hunks -using Git directly, one has to use the very modal and rather clumsy -interface of a ‘git add --interactive’ session. - - With Magit, on the other hand, one can un-/stage individual hunks by -just moving point into the respective section inside a diff displayed in -the status buffer or a separate diff buffer and typing ‘s’ or ‘u’. To -operate on just parts of a hunk, mark the changes that should be -un-/staged using the region and then press the same key that would be -used to un-/stage. To stage multiple files or hunks at once use a -region that starts inside the heading of such a section and ends inside -the heading of a sibling section of the same type. - - Besides staging and unstaging, Magit also provides several other -"apply variants" that can also operate on a file, multiple files at -once, a hunk, multiple hunks at once, and on parts of a hunk. These -apply variants are described in the next section. - - You can also use Ediff to stage and unstage. See *note Ediffing::. - -‘s’ (‘magit-stage’) - Add the change at point to the staging area. - - With a prefix argument and an untracked file (or files) at point, - stage the file but not its content. This makes it possible to - stage only a subset of the new file’s changes. - -‘S’ (‘magit-stage-modified’) - Stage all changes to files modified in the worktree. Stage all new - content of tracked files and remove tracked files that no longer - exist in the working tree from the index also. With a prefix - argument also stage previously untracked (but not ignored) files. - -‘u’ (‘magit-unstage’) - Remove the change at point from the staging area. - - Only staged changes can be unstaged. But by default this command - performs an action that is somewhat similar to unstaging, when it - is called on a committed change: it reverses the change in the - index but not in the working tree. - -‘U’ (‘magit-unstage-all’) - Remove all changes from the staging area. - - -- User Option: magit-unstage-committed - This option controls whether ‘magit-unstage’ "unstages" committed - changes by reversing them in the index but not the working tree. - The alternative is to raise an error. - -‘M-x magit-reverse-in-index’ - This command reverses the committed change at point in the index - but not the working tree. By default no key is bound directly to - this command, but it is indirectly called when ‘u’ - (‘magit-unstage’) is pressed on a committed change. - - This allows extracting a change from ‘HEAD’, while leaving it in - the working tree, so that it can later be committed using a - separate commit. A typical workflow would be: - - 1. Optionally make sure that there are no uncommitted changes. - 2. Visit the ‘HEAD’ commit and navigate to the change that should - not have been included in that commit. - 3. Type ‘u’ (‘magit-unstage’) to reverse it in the index. This - assumes that ‘magit-unstage-committed-changes’ is non-nil. - 4. Type ‘c e’ to extend ‘HEAD’ with the staged changes, including - those that were already staged before. - 5. Optionally stage the remaining changes using ‘s’ or ‘S’ and - then type ‘c c’ to create a new commit. - -‘M-x magit-reset-index’ - Reset the index to some commit. The commit is read from the user - and defaults to the commit at point. If there is no commit at - point, then it defaults to ‘HEAD’. - -* Menu: - -* Staging from File-Visiting Buffers:: - - -File: magit.info, Node: Staging from File-Visiting Buffers, Up: Staging and Unstaging - -6.3.1 Staging from File-Visiting Buffers ----------------------------------------- - -Fine-grained un-/staging has to be done from the status or a diff -buffer, but it’s also possible to un-/stage all changes made to the file -visited in the current buffer right from inside that buffer. - -‘M-x magit-stage-file’ - When invoked inside a file-visiting buffer, then stage all changes - to that file. In a Magit buffer, stage the file at point if any. - Otherwise prompt for a file to be staged. With a prefix argument - always prompt the user for a file, even in a file-visiting buffer - or when there is a file section at point. - -‘M-x magit-unstage-file’ - When invoked inside a file-visiting buffer, then unstage all - changes to that file. In a Magit buffer, unstage the file at point - if any. Otherwise prompt for a file to be unstaged. With a prefix - argument always prompt the user for a file, even in a file-visiting - buffer or when there is a file section at point. - - -File: magit.info, Node: Applying, Next: Committing, Prev: Staging and Unstaging, Up: Manipulating - -6.4 Applying -============ - -Magit provides several "apply variants": stage, unstage, discard, -reverse, and "regular apply". At least when operating on a hunk they -are all implemented using ‘git apply’, which is why they are called -"apply variants". - - • Stage. Apply a change from the working tree to the index. The - change also remains in the working tree. - - • Unstage. Remove a change from the index. The change remains in - the working tree. - - • Discard. On a staged change, remove it from the working tree and - the index. On an unstaged change, remove it from the working tree - only. - - • Reverse. Reverse a change in the working tree. Both committed and - staged changes can be reversed. Unstaged changes cannot be - reversed. Discard them instead. - - • Apply. Apply a change to the working tree. Both committed and - staged changes can be applied. Unstaged changes cannot be applied - - as they already have been applied. - - The previous section described the staging and unstaging commands. -What follows are the commands which implement the remaining apply -variants. - -‘a’ (‘magit-apply’) - Apply the change at point to the working tree. - - With a prefix argument fallback to a 3-way merge. Doing so causes - the change to be applied to the index as well. - -‘k’ (‘magit-discard’) - Remove the change at point from the working tree. - - On a hunk or file with unresolved conflicts prompt which side to - keep (while discarding the other). If point is within the text of - a side, then keep that side without prompting. - -‘v’ (‘magit-reverse’) - Reverse the change at point in the working tree. - - With a prefix argument fallback to a 3-way merge. Doing so causes - the change to be applied to the index as well. - - With a prefix argument all apply variants attempt a 3-way merge when -appropriate (i.e., when ‘git apply’ is used internally). - - -File: magit.info, Node: Committing, Next: Branching, Prev: Applying, Up: Manipulating - -6.5 Committing -============== - -When the user initiates a commit, Magit calls ‘git commit’ without any -arguments, so Git has to get it from the user. It creates the file -‘.git/COMMIT_EDITMSG’ and then opens that file in an editor. Magit -arranges for that editor to be the Emacsclient. Once the user finishes -the editing session, the Emacsclient exits and Git creates the commit -using the file’s content as message. - -* Menu: - -* Initiating a Commit:: -* Editing Commit Messages:: - - -File: magit.info, Node: Initiating a Commit, Next: Editing Commit Messages, Up: Committing - -6.5.1 Initiating a Commit -------------------------- - -Also see *note (gitman)git-commit::. - -‘c’ (‘magit-commit’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - -‘c c’ (‘magit-commit-create’) - Create a new commit on ‘HEAD’. With a prefix argument amend to the - commit at ‘HEAD’ instead. - -‘c a’ (‘magit-commit-amend’) - Amend the last commit. - -‘c e’ (‘magit-commit-extend’) - Amend the last commit, without editing the message. With a prefix - argument keep the committer date, otherwise change it. The option - ‘magit-commit-extend-override-date’ can be used to inverse the - meaning of the prefix argument. - - Non-interactively respect the optional OVERRIDE-DATE argument and - ignore the option. - -‘c w’ (‘magit-commit-reword’) - Reword the last commit, ignoring staged changes. With a prefix - argument keep the committer date, otherwise change it. The option - ‘magit-commit-reword-override-date’ can be used to inverse the - meaning of the prefix argument. - - Non-interactively respect the optional OVERRIDE-DATE argument and - ignore the option. - -‘c f’ (‘magit-commit-fixup’) - Create a fixup commit. - - With a prefix argument the target commit has to be confirmed. - Otherwise the commit at point may be used without confirmation - depending on the value of option ‘magit-commit-squash-confirm’. - -‘c F’ (‘magit-commit-instant-fixup’) - Create a fixup commit and instantly rebase. - -‘c s’ (‘magit-commit-squash’) - Create a squash commit, without editing the squash message. - - With a prefix argument the target commit has to be confirmed. - Otherwise the commit at point may be used without confirmation - depending on the value of option ‘magit-commit-squash-confirm’. - -‘c S’ (‘magit-commit-instant-squash’) - Create a squash commit and instantly rebase. - -‘c A’ (‘magit-commit-augment’) - Create a squash commit, editing the squash message. - - With a prefix argument the target commit has to be confirmed. - Otherwise the commit at point may be used without confirmation - depending on the value of option ‘magit-commit-squash-confirm’. - - -- User Option: magit-commit-ask-to-stage - Whether to ask to stage all unstaged changes when committing and - nothing is staged. - - -- User Option: magit-commit-show-diff - Whether the relevant diff is automatically shown when committing. - - -- User Option: magit-commit-extend-override-date - Whether using ‘magit-commit-extend’ changes the committer date. - - -- User Option: magit-commit-reword-override-date - Whether using ‘magit-commit-reword’ changes the committer date. - - -- User Option: magit-commit-squash-confirm - Whether the commit targeted by squash and fixup has to be - confirmed. When non-nil then the commit at point (if any) is used - as default choice. Otherwise it has to be confirmed. This option - only affects ‘magit-commit-squash’ and ‘magit-commit-fixup’. The - "instant" variants always require confirmation because making an - error while using those is harder to recover from. - - -- User Option: magit-post-commit-hook - Hook run after creating a commit without the user editing a - message. - - This hook is run by ‘magit-refresh’ if ‘this-command’ is a member - of ‘magit-post-commit-hook-commands’. This only includes commands - named ‘magit-commit-*’ that do *not* require that the user edits - the commit message in a buffer. - - Also see ‘git-commit-post-finish-hook’. - - -- User Option: magit-commit-diff-inhibit-same-window - Whether to inhibit use of same window when showing diff while - committing. - - When writing a commit, then a diff of the changes to be committed - is automatically shown. The idea is that the diff is shown in a - different window of the same frame and for most users that just - works. In other words most users can completely ignore this option - because its value doesn’t make a difference for them. - - However for users who configured Emacs to never create a new window - even when the package explicitly tries to do so, then displaying - two new buffers necessarily means that the first is immediately - replaced by the second. In our case the message buffer is - immediately replaced by the diff buffer, which is of course highly - undesirable. - - A workaround is to suppress this user configuration in this - particular case. Users have to explicitly opt-in by toggling this - option. We cannot enable the workaround unconditionally because - that again causes issues for other users: if the frame is too tiny - or the relevant settings too aggressive, then the diff buffer would - end up being displayed in a new frame. - - Also see <https://github.com/magit/magit/issues/4132>. - - -File: magit.info, Node: Editing Commit Messages, Prev: Initiating a Commit, Up: Committing - -6.5.2 Editing Commit Messages ------------------------------ - -After initiating a commit as described in the previous section, two new -buffers appear. One shows the changes that are about to be committed, -while the other is used to write the message. - - Commit messages are edited in an edit session - in the background -‘git’ is waiting for the editor, in our case ‘emacsclient’, to save the -commit message in a file (in most cases ‘.git/COMMIT_EDITMSG’) and then -return. If the editor returns with a non-zero exit status then ‘git’ -does not create the commit. So the most important commands are those -for finishing and aborting the commit. - -‘C-c C-c’ (‘with-editor-finish’) - Finish the current editing session by returning with exit code 0. - Git then creates the commit using the message it finds in the file. - -‘C-c C-k’ (‘with-editor-cancel’) - Cancel the current editing session by returning with exit code 1. - Git then cancels the commit, but leaves the file untouched. - - In addition to being used by ‘git commit’, messages may also be -stored in a ring that persists until Emacs is closed. By default the -message is stored at the beginning and the end of an edit session -(regardless of whether the session is finished successfully or was -canceled). It is sometimes useful to bring back messages from that -ring. - -‘C-c M-s’ (‘git-commit-save-message’) - Save the current buffer content to the commit message ring. - -‘M-p’ (‘git-commit-prev-message’) - Cycle backward through the commit message ring, after saving the - current message to the ring. With a numeric prefix ARG, go back - ARG comments. - -‘M-n’ (‘git-commit-next-message’) - Cycle forward through the commit message ring, after saving the - current message to the ring. With a numeric prefix ARG, go back - ARG comments. - - By default the diff for the changes that are about to be committed -are automatically shown when invoking the commit. To prevent that, -remove ‘magit-commit-diff’ from ‘server-switch-hook’. - - When amending to an existing commit it may be useful to show either -the changes that are about to be added to that commit or to show those -changes alongside those that have already been committed. - -‘C-c C-d’ (‘magit-diff-while-committing’) - While committing, show the changes that are about to be committed. - While amending, invoking the command again toggles between showing - just the new changes or all the changes that will be committed. - -* Menu: - -* Using the Revision Stack:: -* Commit Pseudo Headers:: -* Commit Mode and Hooks:: -* Commit Message Conventions:: - - -File: magit.info, Node: Using the Revision Stack, Next: Commit Pseudo Headers, Up: Editing Commit Messages - -Using the Revision Stack -........................ - -‘C-c C-w’ (‘magit-pop-revision-stack’) - This command inserts a representation of a revision into the - current buffer. It can be used inside buffers used to write commit - messages but also in other buffers such as buffers used to edit - emails or ChangeLog files. - - By default this command pops the revision which was last added to - the ‘magit-revision-stack’ and inserts it into the current buffer - according to ‘magit-pop-revision-stack-format’. Revisions can be - put on the stack using ‘magit-copy-section-value’ and - ‘magit-copy-buffer-revision’. - - If the stack is empty or with a prefix argument it instead reads a - revision in the minibuffer. By using the minibuffer history this - allows selecting an item which was popped earlier or to insert an - arbitrary reference or revision without first pushing it onto the - stack. - - When reading the revision from the minibuffer, then it might not be - possible to guess the correct repository. When this command is - called inside a repository (e.g., while composing a commit - message), then that repository is used. Otherwise (e.g., while - composing an email) then the repository recorded for the top - element of the stack is used (even though we insert another - revision). If not called inside a repository and with an empty - stack, or with two prefix arguments, then read the repository in - the minibuffer too. - - -- User Option: magit-pop-revision-stack-format - This option controls how the command ‘magit-pop-revision-stack’ - inserts a revision into the current buffer. - - The entries on the stack have the format ‘(HASH TOPLEVEL)’ and this - option has the format ‘(POINT-FORMAT EOB-FORMAT INDEX-REGEXP)’, all - of which may be nil or a string (though either one of EOB-FORMAT or - POINT-FORMAT should be a string, and if INDEX-REGEXP is non-nil, - then the two formats should be too). - - First INDEX-REGEXP is used to find the previously inserted entry, - by searching backward from point. The first submatch must match - the index number. That number is incremented by one, and becomes - the index number of the entry to be inserted. If you don’t want to - number the inserted revisions, then use nil for INDEX-REGEXP. - - If INDEX-REGEXP is non-nil then both POINT-FORMAT and EOB-FORMAT - should contain \"%N\", which is replaced with the number that was - determined in the previous step. - - Both formats, if non-nil and after removing %N, are then expanded - using ‘git show --format=FORMAT ...’ inside TOPLEVEL. - - The expansion of POINT-FORMAT is inserted at point, and the - expansion of EOB-FORMAT is inserted at the end of the buffer (if - the buffer ends with a comment, then it is inserted right before - that). - - -File: magit.info, Node: Commit Pseudo Headers, Next: Commit Mode and Hooks, Prev: Using the Revision Stack, Up: Editing Commit Messages - -Commit Pseudo Headers -..................... - -Some projects use pseudo headers in commit messages. Magit colorizes -such headers and provides some commands to insert such headers. - - -- User Option: git-commit-known-pseudo-headers - A list of Git pseudo headers to be highlighted. - -‘C-c C-i’ (‘git-commit-insert-pseudo-header’) - Insert a commit message pseudo header. - -‘C-c C-a’ (‘git-commit-ack’) - Insert a header acknowledging that you have looked at the commit. - -‘C-c C-r’ (‘git-commit-review’) - Insert a header acknowledging that you have reviewed the commit. - -‘C-c C-s’ (‘git-commit-signoff’) - Insert a header to sign off the commit. - -‘C-c C-t’ (‘git-commit-test’) - Insert a header acknowledging that you have tested the commit. - -‘C-c C-o’ (‘git-commit-cc’) - Insert a header mentioning someone who might be interested. - -‘C-c C-p’ (‘git-commit-reported’) - Insert a header mentioning the person who reported the issue being - fixed by the commit. - -‘C-c M-i’ (‘git-commit-suggested’) - Insert a header mentioning the person who suggested the change. - - -File: magit.info, Node: Commit Mode and Hooks, Next: Commit Message Conventions, Prev: Commit Pseudo Headers, Up: Editing Commit Messages - -Commit Mode and Hooks -..................... - -‘git-commit-mode’ is a minor mode that is only used to establish certain -key bindings. This makes it possible to use an arbitrary major mode in -buffers used to edit commit messages. It is even possible to use -different major modes in different repositories, which is useful when -different projects impose different commit message conventions. - - -- User Option: git-commit-major-mode - The value of this option is the major mode used to edit Git commit - messages. - - Because ‘git-commit-mode’ is a minor mode, we don’t use its mode hook -to setup the buffer, except for the key bindings. All other setup -happens in the function ‘git-commit-setup’, which among other things -runs the hook ‘git-commit-setup-hook’. - - -- User Option: git-commit-setup-hook - Hook run at the end of ‘git-commit-setup’. - -The following functions are suitable for this hook: - - -- Function: git-commit-save-message - Save the current buffer content to the commit message ring. - - -- Function: git-commit-setup-changelog-support - After this function is called, ChangeLog entries are treated as - paragraphs. - - -- Function: git-commit-turn-on-auto-fill - Turn on ‘auto-fill-mode’. - - -- Function: git-commit-turn-on-flyspell - Turn on Flyspell mode. Also prevent comments from being checked - and finally check current non-comment text. - - -- Function: git-commit-propertize-diff - Propertize the diff shown inside the commit message buffer. Git - inserts such diffs into the commit message template when the - ‘--verbose’ argument is used. ‘magit-commit’ by default does not - offer that argument because the diff that is shown in a separate - buffer is more useful. But some users disagree, which is why this - function exists. - - -- Function: bug-reference-mode - Hyperlink bug references in the buffer. - - -- Function: with-editor-usage-message - Show usage information in the echo area. - - -- User Option: git-commit-post-finish-hook - Hook run after the user finished writing a commit message. - - This hook is only run after pressing ‘C-c C-c’ in a buffer used to - edit a commit message. If a commit is created without the user - typing a message into a buffer, then this hook is not run. - - This hook is not run until the new commit has been created. If - doing so takes Git longer than one second, then this hook isn’t run - at all. For certain commands such as ‘magit-rebase-continue’ this - hook is never run because doing so would lead to a race condition. - - This hook is only run if ‘magit’ is available. - - Also see ‘magit-post-commit-hook’. - - -File: magit.info, Node: Commit Message Conventions, Prev: Commit Mode and Hooks, Up: Editing Commit Messages - -Commit Message Conventions -.......................... - -Git-Commit highlights certain violations of commonly accepted commit -message conventions. Certain violations even cause Git-Commit to ask -you to confirm that you really want to do that. This nagging can of -course be turned off, but the result of doing that usually is that -instead of some code it’s now the human who is reviewing your commits -who has to waste some time telling you to fix your commits. - - -- User Option: git-commit-summary-max-length - The intended maximal length of the summary line of commit messages. - Characters beyond this column are colorized to indicate that this - preference has been violated. - - -- User Option: git-commit-finish-query-functions - List of functions called to query before performing commit. - - The commit message buffer is current while the functions are - called. If any of them returns nil, then the commit is not - performed and the buffer is not killed. The user should then fix - the issue and try again. - - The functions are called with one argument. If it is non-nil then - that indicates that the user used a prefix argument to force - finishing the session despite issues. Functions should usually - honor this wish and return non-nil. - - By default the only member is ‘git-commit-check-style-conventions’. - - -- Function: git-commit-check-style-conventions - This function checks for violations of certain basic style - conventions. For each violation it asks users if they want to - proceed anyway. - - -- User Option: git-commit-style-convention-checks - This option controls what conventions the function by the same name - tries to enforce. The value is a list of self-explanatory symbols - identifying certain conventions; ‘non-empty-second-line’ and - ‘overlong-summary-line’. - - -File: magit.info, Node: Branching, Next: Merging, Prev: Committing, Up: Manipulating - -6.6 Branching -============= - -* Menu: - -* The Two Remotes:: -* Branch Commands:: -* Branch Git Variables:: -* Auxiliary Branch Commands:: - - -File: magit.info, Node: The Two Remotes, Next: Branch Commands, Up: Branching - -6.6.1 The Two Remotes ---------------------- - -The upstream branch of some local branch is the branch into which the -commits on that local branch should eventually be merged, usually -something like ‘origin/master’. For the ‘master’ branch itself the -upstream branch and the branch it is being pushed to, are usually the -same remote branch. But for a feature branch the upstream branch and -the branch it is being pushed to should differ. - - The commits on feature branches too should _eventually_ end up in a -remote branch such as ‘origin/master’ or ‘origin/maint’. Such a branch -should therefore be used as the upstream. But feature branches -shouldn’t be pushed directly to such branches. Instead a feature branch -‘my-feature’ is usually pushed to ‘my-fork/my-feature’ or if you are a -contributor ‘origin/my-feature’. After the new feature has been -reviewed, the maintainer merges the feature into ‘master’. And finally -‘master’ (not ‘my-feature’ itself) is pushed to ‘origin/master’. - - But new features seldom are perfect on the first try, and so feature -branches usually have to be reviewed, improved, and re-pushed several -times. Pushing should therefore be easy to do, and for that reason many -Git users have concluded that it is best to use the remote branch to -which the local feature branch is being pushed as its upstream. - - But luckily Git has long ago gained support for a push-remote which -can be configured separately from the upstream branch, using the -variables ‘branch.<name>.pushRemote’ and ‘remote.pushDefault’. So we no -longer have to choose which of the two remotes should be used as "the -remote". - - Each of the fetching, pulling, and pushing transient commands -features three suffix commands that act on the current branch and some -other branch. Of these, ‘p’ is bound to a command which acts on the -push-remote, ‘u’ is bound to a command which acts on the upstream, and -‘e’ is bound to a command which acts on any other branch. The status -buffer shows unpushed and unpulled commits for both the push-remote and -the upstream. - - It’s fairly simple to configure these two remotes. The values of all -the variables that are related to fetching, pulling, and pushing (as -well as some other branch-related variables) can be inspected and -changed using the command ‘magit-branch-configure’, which is available -from many transient prefix commands that deal with branches. It is also -possible to set the push-remote or upstream while pushing (see *note -Pushing::). - - -File: magit.info, Node: Branch Commands, Next: Branch Git Variables, Prev: The Two Remotes, Up: Branching - -6.6.2 Branch Commands ---------------------- - -The transient prefix command ‘magit-branch’ is used to create and -checkout branches, and to make changes to existing branches. It is not -used to fetch, pull, merge, rebase, or push branches, i.e., this command -deals with branches themselves, not with the commits reachable from -them. Those features are available from separate transient command. - -‘b’ (‘magit-branch’) - This transient prefix command binds the following suffix commands - and displays them in a temporary buffer until a suffix is invoked. - - By default it also binds and displays the values of some - branch-related Git variables and allows changing their values. - - -- User Option: magit-branch-direct-configure - This option controls whether the transient command ‘magit-branch’ - can be used to directly change the values of Git variables. This - defaults to ‘t’ (to avoid changing key bindings). When set to - ‘nil’, then no variables are displayed by that transient command, - and its suffix command ‘magit-branch-configure’ has to be used - instead to view and change branch related variables. - -‘b C’ (‘magit-branch-configure’) -‘f C’ -‘F C’ -‘P C’ - This transient prefix command binds commands that set the value of - branch-related variables and displays them in a temporary buffer - until the transient is exited. - - With a prefix argument, this command always prompts for a branch. - - Without a prefix argument this depends on whether it was invoked as - a suffix of ‘magit-branch’ and on the - ‘magit-branch-direct-configure’ option. If ‘magit-branch’ already - displays the variables for the current branch, then it isn’t useful - to invoke another transient that displays them for the same branch. - In that case this command prompts for a branch. - - The variables are described in *note Branch Git Variables::. - -‘b b’ (‘magit-checkout’) - Checkout a revision read in the minibuffer and defaulting to the - branch or arbitrary revision at point. If the revision is a local - branch then that becomes the current branch. If it is something - else then ‘HEAD’ becomes detached. Checkout fails if the working - tree or the staging area contain changes. - -‘b n’ (‘magit-branch-create’) - Create a new branch. The user is asked for a branch or arbitrary - revision to use as the starting point of the new branch. When a - branch name is provided, then that becomes the upstream branch of - the new branch. The name of the new branch is also read in the - minibuffer. - - Also see option ‘magit-branch-prefer-remote-upstream’. - -‘b c’ (‘magit-branch-and-checkout’) - This command creates a new branch like ‘magit-branch-create’, but - then also checks it out. - - Also see option ‘magit-branch-prefer-remote-upstream’. - -‘b l’ (‘magit-branch-checkout’) - This command checks out an existing or new local branch. It reads - a branch name from the user offering all local branches and a - subset of remote branches as candidates. Remote branches for which - a local branch by the same name exists are omitted from the list of - candidates. The user can also enter a completely new branch name. - - • If the user selects an existing local branch, then that is - checked out. - - • If the user selects a remote branch, then it creates and - checks out a new local branch with the same name, and - configures the selected remote branch as the push target. - - • If the user enters a new branch name, then it creates and - checks that out, after also reading the starting-point from - the user. - - In the latter two cases the upstream is also set. Whether it is - set to the chosen starting point or something else depends on the - value of ‘magit-branch-adjust-remote-upstream-alist’. - -‘b s’ (‘magit-branch-spinoff’) - This command creates and checks out a new branch starting at and - tracking the current branch. That branch in turn is reset to the - last commit it shares with its upstream. If the current branch has - no upstream or no unpushed commits, then the new branch is created - anyway and the previously current branch is not touched. - - This is useful to create a feature branch after work has already - began on the old branch (likely but not necessarily "master"). - - If the current branch is a member of the value of option - ‘magit-branch-prefer-remote-upstream’ (which see), then the current - branch will be used as the starting point as usual, but the - upstream of the starting-point may be used as the upstream of the - new branch, instead of the starting-point itself. - - If optional FROM is non-nil, then the source branch is reset to - ‘FROM~’, instead of to the last commit it shares with its upstream. - Interactively, FROM is only ever non-nil, if the region selects - some commits, and among those commits, FROM is the commit that is - the fewest commits ahead of the source branch. - - The commit at the other end of the selection actually does not - matter, all commits between FROM and ‘HEAD’ are moved to the new - branch. If FROM is not reachable from ‘HEAD’ or is reachable from - the source branch’s upstream, then an error is raised. - -‘b S’ (‘magit-branch-spinout’) - This command behaves like ‘magit-branch-spinoff’, except that it - does not change the current branch. If there are any uncommitted - changes, then it behaves exactly like ‘magit-branch-spinoff’. - -‘b x’ (‘magit-branch-reset’) - This command resets a branch, defaulting to the branch at point, to - the tip of another branch or any other commit. - - When the branch being reset is the current branch, then a hard - reset is performed. If there are any uncommitted changes, then the - user has to confirm the reset because those changes would be lost. - - This is useful when you have started work on a feature branch but - realize it’s all crap and want to start over. - - When resetting to another branch and a prefix argument is used, - then the target branch is set as the upstream of the branch that is - being reset. - -‘b k’ (‘magit-branch-delete’) - Delete one or multiple branches. If the region marks multiple - branches, then offer to delete those. Otherwise, prompt for a - single branch to be deleted, defaulting to the branch at point. - - Require confirmation when deleting branches is dangerous in some - way. Option ‘magit-no-confirm’ can be customized to not require - confirmation in certain cases. See its docstring to learn why - confirmation is required by default in certain cases or if a prompt - is confusing. - -‘b m’ (‘magit-branch-rename’) - Rename a branch. The branch and the new name are read in the - minibuffer. With prefix argument the branch is renamed even if - that name conflicts with an existing branch. - - -- User Option: magit-branch-read-upstream-first - When creating a branch, whether to read the upstream branch before - the name of the branch that is to be created. The default is ‘t’, - and I recommend you leave it at that. - - -- User Option: magit-branch-prefer-remote-upstream - This option specifies whether remote upstreams are favored over - local upstreams when creating new branches. - - When a new branch is created, then the branch, commit, or stash at - point is suggested as the starting point of the new branch, or if - there is no such revision at point the current branch. In either - case the user may choose another starting point. - - If the chosen starting point is a branch, then it may also be set - as the upstream of the new branch, depending on the value of the - Git variable ‘branch.autoSetupMerge’. By default this is done for - remote branches, but not for local branches. - - You might prefer to always use some remote branch as upstream. If - the chosen starting point is (1) a local branch, (2) whose name - matches a member of the value of this option, (3) the upstream of - that local branch is a remote branch with the same name, and (4) - that remote branch can be fast-forwarded to the local branch, then - the chosen branch is used as starting point, but its own upstream - is used as the upstream of the new branch. - - Members of this option’s value are treated as branch names that - have to match exactly unless they contain a character that makes - them invalid as a branch name. Recommended characters to use to - trigger interpretation as a regexp are "*" and "^". Some other - characters which you might expect to be invalid, actually are not, - e.g., ".+$" are all perfectly valid. More precisely, if ‘git - check-ref-format --branch STRING’ exits with a non-zero status, - then treat STRING as a regexp. - - Assuming the chosen branch matches these conditions you would end - up with with e.g.: - - feature --upstream--> origin/master - - instead of - - feature --upstream--> master --upstream--> origin/master - - Which you prefer is a matter of personal preference. If you do - prefer the former, then you should add branches such as ‘master’, - ‘next’, and ‘maint’ to the value of this options. - - -- User Option: magit-branch-adjust-remote-upstream-alist - The value of this option is an alist of branches to be used as the - upstream when branching a remote branch. - - When creating a local branch from an ephemeral branch located on a - remote, e.g., a feature or hotfix branch, then that remote branch - should usually not be used as the upstream branch, since the - push-remote already allows accessing it and having both the - upstream and the push-remote reference the same related branch - would be wasteful. Instead a branch like "maint" or "master" - should be used as the upstream. - - This option allows specifying the branch that should be used as the - upstream when branching certain remote branches. The value is an - alist of the form ‘((UPSTREAM . RULE)...)’. The first matching - element is used, the following elements are ignored. - - UPSTREAM is the branch to be used as the upstream for branches - specified by RULE. It can be a local or a remote branch. - - RULE can either be a regular expression, matching branches whose - upstream should be the one specified by UPSTREAM. Or it can be a - list of the only branches that should *not* use UPSTREAM; all other - branches will. Matching is done after stripping the remote part of - the name of the branch that is being branched from. - - If you use a finite set of non-ephemeral branches across all your - repositories, then you might use something like: - - (("origin/master" . ("master" "next" "maint"))) - - Or if the names of all your ephemeral branches contain a slash, at - least in some repositories, then a good value could be: - - (("origin/master" . "/")) - - Of course you can also fine-tune: - - (("origin/maint" . "\\`hotfix/") - ("origin/master" . "\\`feature/")) - - UPSTREAM can be a local branch: - - (("master" . ("master" "next" "maint"))) - - Because the main branch is no longer almost always named "master" you -should also account for other common names: - - (("main" . ("main" "master" "next" "maint")) - ("master" . ("main" "master" "next" "maint"))) - - -- Command: magit-branch-orphan - This command creates and checks out a new orphan branch with - contents from a given revision. - - -- Command: magit-branch-or-checkout - This command is a hybrid between ‘magit-checkout’ and - ‘magit-branch-and-checkout’ and is intended as a replacement for - the former in ‘magit-branch’. - - It first asks the user for an existing branch or revision. If the - user input actually can be resolved as a branch or revision, then - it checks that out, just like ‘magit-checkout’ would. - - Otherwise it creates and checks out a new branch using the input as - its name. Before doing so it reads the starting-point for the new - branch. This is similar to what ‘magit-branch-and-checkout’ does. - - To use this command instead of ‘magit-checkout’ add this to your - init file: - - (transient-replace-suffix 'magit-branch 'magit-checkout - '("b" "dwim" magit-branch-or-checkout)) - - -File: magit.info, Node: Branch Git Variables, Next: Auxiliary Branch Commands, Prev: Branch Commands, Up: Branching - -6.6.3 Branch Git Variables --------------------------- - -These variables can be set from the transient prefix command -‘magit-branch-configure’. By default they can also be set from -‘magit-branch’. See *note Branch Commands::. - - -- Variable: branch.NAME.merge - Together with ‘branch.NAME.remote’ this variable defines the - upstream branch of the local branch named NAME. The value of this - variable is the full reference of the upstream _branch_. - - -- Variable: branch.NAME.remote - Together with ‘branch.NAME.merge’ this variable defines the - upstream branch of the local branch named NAME. The value of this - variable is the name of the upstream _remote_. - - -- Variable: branch.NAME.rebase - This variable controls whether pulling into the branch named NAME - is done by rebasing or by merging the fetched branch. - - • When ‘true’ then pulling is done by rebasing. - • When ‘false’ then pulling is done by merging. - • When undefined then the value of ‘pull.rebase’ is used. The - default of that variable is ‘false’. - - -- Variable: branch.NAME.pushRemote - This variable specifies the remote that the branch named NAME is - usually pushed to. The value has to be the name of an existing - remote. - - It is not possible to specify the name of _branch_ to push the - local branch to. The name of the remote branch is always the same - as the name of the local branch. - - If this variable is undefined but ‘remote.pushDefault’ is defined, - then the value of the latter is used. By default - ‘remote.pushDefault’ is undefined. - - -- Variable: branch.NAME.description - This variable can be used to describe the branch named NAME. That - description is used, e.g., when turning the branch into a series of - patches. - - The following variables specify defaults which are used if the above -branch-specific variables are not set. - - -- Variable: pull.rebase - This variable specifies whether pulling is done by rebasing or by - merging. It can be overwritten using ‘branch.NAME.rebase’. - - • When ‘true’ then pulling is done by rebasing. - • When ‘false’ (the default) then pulling is done by merging. - - Since it is never a good idea to merge the upstream branch into a - feature or hotfix branch and most branches are such branches, you - should consider setting this to ‘true’, and ‘branch.master.rebase’ - to ‘false’. - - -- Variable: remote.pushDefault - This variable specifies what remote the local branches are usually - pushed to. This can be overwritten per branch using - ‘branch.NAME.pushRemote’. - - The following variables are used during the creation of a branch and -control whether the various branch-specific variables are automatically -set at this time. - - -- Variable: branch.autoSetupMerge - This variable specifies under what circumstances creating a branch - NAME should result in the variables ‘branch.NAME.merge’ and - ‘branch.NAME.remote’ being set according to the starting point used - to create the branch. If the starting point isn’t a branch, then - these variables are never set. - - • When ‘always’ then the variables are set regardless of whether - the starting point is a local or a remote branch. - • When ‘true’ (the default) then the variables are set when the - starting point is a remote branch, but not when it is a local - branch. - • When ‘false’ then the variables are never set. - - -- Variable: branch.autoSetupRebase - This variable specifies whether creating a branch NAME should - result in the variable ‘branch.NAME.rebase’ being set to ‘true’. - - • When ‘always’ then the variable is set regardless of whether - the starting point is a local or a remote branch. - • When ‘local’ then the variable are set when the starting point - is a local branch, but not when it is a remote branch. - • When ‘remote’ then the variable are set when the starting - point is a remote branch, but not when it is a local branch. - • When ‘never’ (the default) then the variable is never set. - - Note that the respective commands always change the repository-local -values. If you want to change the global value, which is used when the -local value is undefined, then you have to do so on the command line, -e.g.: - - git config --global remote.autoSetupMerge always - - For more information about these variables you should also see - - *note (gitman)git-config::. Also see *note (gitman)git-branch::. , -*note (gitman)git-checkout::. and *note Pushing::. - - -- User Option: magit-prefer-remote-upstream - This option controls whether commands that read a branch from the - user and then set it as the upstream branch, offer a local or a - remote branch as default completion candidate, when they have the - choice. - - This affects all commands that use ‘magit-read-upstream-branch’ or - ‘magit-read-starting-point’, which includes all commands that - change the upstream and many which create new branches. - - -File: magit.info, Node: Auxiliary Branch Commands, Prev: Branch Git Variables, Up: Branching - -6.6.4 Auxiliary Branch Commands -------------------------------- - -These commands are not available from the transient ‘magit-branch’ by -default. - - -- Command: magit-branch-shelve - This command shelves a branch. This is done by deleting the - branch, and creating a new reference "refs/shelved/BRANCH-NAME" - pointing at the same commit as the branch pointed at. If the - deleted branch had a reflog, then that is preserved as the reflog - of the new reference. - - This is useful if you want to move a branch out of sight, but are - not ready to completely discard it yet. - - -- Command: magit-branch-unshelve - This command unshelves a branch that was previously shelved using - ‘magit-branch-shelve’. This is done by deleting the reference - "refs/shelved/BRANCH-NAME" and creating a branch "BRANCH-NAME" - pointing at the same commit as the deleted reference pointed at. - If the deleted reference had a reflog, then that is restored as the - reflog of the branch. - - -File: magit.info, Node: Merging, Next: Resolving Conflicts, Prev: Branching, Up: Manipulating - -6.7 Merging -=========== - -Also see *note (gitman)git-merge::. For information on how to resolve -merge conflicts see the next section. - -‘m’ (‘magit-merge’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - - When no merge is in progress, then the transient features the -following suffix commands. - -‘m m’ (‘magit-merge-plain’) - This command merges another branch or an arbitrary revision into - the current branch. The branch or revision to be merged is read in - the minibuffer and defaults to the branch at point. - - Unless there are conflicts or a prefix argument is used, then the - resulting merge commit uses a generic commit message, and the user - does not get a chance to inspect or change it before the commit is - created. With a prefix argument this does not actually create the - merge commit, which makes it possible to inspect how conflicts were - resolved and to adjust the commit message. - -‘m e’ (‘magit-merge-editmsg’) - This command merges another branch or an arbitrary revision into - the current branch and opens a commit message buffer, so that the - user can make adjustments. The commit is not actually created - until the user finishes with ‘C-c C-c’. - -‘m n’ (‘magit-merge-nocommit’) - This command merges another branch or an arbitrary revision into - the current branch, but does not actually create the merge commit. - The user can then further adjust the merge, even when automatic - conflict resolution succeeded and/or adjust the commit message. - -‘m a’ (‘magit-merge-absorb’) - This command merges another local branch into the current branch - and then removes the former. - - Before the source branch is merged, it is first force pushed to its - push-remote, provided the respective remote branch already exists. - This ensures that the respective pull-request (if any) won’t get - stuck on some obsolete version of the commits that are being - merged. Finally, if ‘magit-branch-pull-request’ was used to create - the merged branch, then the respective remote branch is also - removed. - -‘m i’ (‘magit-merge-into’) - This command merges the current branch into another local branch - and then removes the former. The latter becomes the new current - branch. - - Before the source branch is merged, it is first force pushed to its - push-remote, provided the respective remote branch already exists. - This ensures that the respective pull-request (if any) won’t get - stuck on some obsolete version of the commits that are being - merged. Finally, if ‘magit-branch-pull-request’ was used to create - the merged branch, then the respective remote branch is also - removed. - -‘m s’ (‘magit-merge-squash’) - This command squashes the changes introduced by another branch or - an arbitrary revision into the current branch. This only applies - the changes made by the squashed commits. No information is - preserved that would allow creating an actual merge commit. - Instead of this command you should probably use a command from the - apply transient. - -‘m p’ (‘magit-merge-preview’) - This command shows a preview of merging another branch or an - arbitrary revision into the current branch. - - Note that commands, that normally change how a diff is displayed, - do not work in buffers created by this command, because the - underlying Git command does not support diff arguments. - - When a merge is in progress, then the transient instead features the -following suffix commands. - -‘m m’ (‘magit-merge’) - After the user resolved conflicts, this command proceeds with the - merge. If some conflicts weren’t resolved, then this command - fails. - -‘m a’ (‘magit-merge-abort’) - This command aborts the current merge operation. - - -File: magit.info, Node: Resolving Conflicts, Next: Rebasing, Prev: Merging, Up: Manipulating - -6.8 Resolving Conflicts -======================= - -When merging branches (or otherwise combining or changing history) -conflicts can occur. If you edited two completely different parts of -the same file in two branches and then merge one of these branches into -the other, then Git can resolve that on its own, but if you edit the -same area of a file, then a human is required to decide how the two -versions, or "sides of the conflict", are to be combined into one. - - Here we can only provide a brief introduction to the subject and -point you toward some tools that can help. If you are new to this, then -please also consult Git’s own documentation as well as other resources. - - If a file has conflicts and Git cannot resolve them by itself, then -it puts both versions into the affected file along with special markers -whose purpose is to denote the boundaries of the unresolved part of the -file and between the different versions. These boundary lines begin -with the strings consisting of seven times the same character, one of -‘<’, ‘|’, ‘=’ and ‘>’, and are followed by information about the source -of the respective versions, e.g.: - - <<<<<<< HEAD - Take the blue pill. - ======= - Take the red pill. - >>>>>>> feature - - In this case you have chosen to take the red pill on one branch and -on another you picked the blue pill. Now that you are merging these two -diverging branches, Git cannot possibly know which pill you want to -take. - - To resolve that conflict you have to create a version of the affected -area of the file by keeping only one of the sides, possibly by editing -it in order to bring in the changes from the other side, remove the -other versions as well as the markers, and then stage the result. A -possible resolution might be: - - Take both pills. - - Often it is useful to see not only the two sides of the conflict but -also the "original" version from before the same area of the file was -modified twice on different branches. Instruct Git to insert that -version as well by running this command once: - - git config --global merge.conflictStyle diff3 - - The above conflict might then have looked like this: - - <<<<<<< HEAD - Take the blue pill. - ||||||| merged common ancestors - Take either the blue or the red pill, but not both. - ======= - Take the red pill. - >>>>>>> feature - - If that were the case, then the above conflict resolution would not -have been correct, which demonstrates why seeing the original version -alongside the conflicting versions can be useful. - - You can perform the conflict resolution completely by hand, but Emacs -also provides some packages that help in the process: Smerge, Ediff -(*note (ediff)Top::), and Emerge (*note (emacs)Emerge::). Magit does -not provide its own tools for conflict resolution, but it does make -using Smerge and Ediff more convenient. (Ediff supersedes Emerge, so -you probably don’t want to use the latter anyway.) - - In the Magit status buffer, files with unresolved conflicts are -listed in the "Unstaged changes" and/or "Staged changes" sections. They -are prefixed with the word "unmerged", which in this context essentially -is a synonym for "unresolved". - - Pressing ‘RET’ while point is on such a file section shows a buffer -visiting that file, turns on ‘smerge-mode’ in that buffer, and places -point inside the first area with conflicts. You should then resolve -that conflict using regular edit commands and/or Smerge commands. - - Unfortunately Smerge does not have a manual, but you can get a list -of commands and binding ‘C-c ^ C-h’ and press ‘RET’ while point is on a -command name to read its documentation. - - Normally you would edit one version and then tell Smerge to keep only -that version. Use ‘C-c ^ m’ (‘smerge-keep-mine’) to keep the ‘HEAD’ -version or ‘C-c ^ o’ (‘smerge-keep-other’) to keep the version that -follows "|||||||". Then use ‘C-c ^ n’ to move to the next conflicting -area in the same file. Once you are done resolving conflicts, return to -the Magit status buffer. The file should now be shown as "modified", no -longer as "unmerged", because Smerge automatically stages the file when -you save the buffer after resolving the last conflict. - - Magit now wraps the mentioned Smerge commands, allowing you to use -these key bindings without having to go to the file-visiting buffer. -Additionally ‘k’ (‘magit-discard’) on a hunk with unresolved conflicts -asks which side to keep or, if point is on a side, then it keeps it -without prompting. Similarly ‘k’ on a unresolved file ask which side to -keep. - - Alternatively you could use Ediff, which uses separate buffers for -the different versions of the file. To resolve conflicts in a file -using Ediff press ‘e’ while point is on such a file in the status -buffer. - - Ediff can be used for other purposes as well. For more information -on how to enter Ediff from Magit, see *note Ediffing::. Explaining how -to use Ediff is beyond the scope of this manual, instead see *note -(ediff)Top::. - - If you are unsure whether you should Smerge or Ediff, then use the -former. It is much easier to understand and use, and except for truly -complex conflicts, the latter is usually overkill. - - -File: magit.info, Node: Rebasing, Next: Cherry Picking, Prev: Resolving Conflicts, Up: Manipulating - -6.9 Rebasing -============ - -Also see *note (gitman)git-rebase::. For information on how to resolve -conflicts that occur during rebases see the preceding section. - -‘r’ (‘magit-rebase’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - - When no rebase is in progress, then the transient features the -following suffix commands. - - Using one of these commands _starts_ a rebase sequence. Git might -then stop somewhere along the way, either because you told it to do so, -or because applying a commit failed due to a conflict. When that -happens, then the status buffer shows information about the rebase -sequence which is in progress in a section similar to a log section. -See *note Information About In-Progress Rebase::. - - For information about the upstream and the push-remote, see *note The -Two Remotes::. - -‘r p’ (‘magit-rebase-onto-pushremote’) - This command rebases the current branch onto its push-remote. - - With a prefix argument or when the push-remote is either not - configured or unusable, then let the user first configure the - push-remote. - -‘r u’ (‘magit-rebase-onto-upstream’) - This command rebases the current branch onto its upstream branch. - - With a prefix argument or when the upstream is either not - configured or unusable, then let the user first configure the - upstream. - -‘r e’ (‘magit-rebase-branch’) - This command rebases the current branch onto a branch read in the - minibuffer. All commits that are reachable from head but not from - the selected branch TARGET are being rebased. - -‘r s’ (‘magit-rebase-subset’) - This command starts a non-interactive rebase sequence to transfer - commits from START to ‘HEAD’ onto NEWBASE. START has to be - selected from a list of recent commits. - - By default Magit uses the ‘--autostash’ argument, which causes -uncommitted changes to be stored in a stash before the rebase begins. -These changes are restored after the rebase completes and if possible -the stash is removed. If the stash does not apply cleanly, then the -stash is not removed. In case something goes wrong when resolving the -conflicts, this allows you to start over. - - Even though one of the actions is dedicated to interactive rebases, -the transient also features the infix argument ‘--interactive’. This -can be used to turn one of the other, non-interactive rebase variants -into an interactive rebase. - - For example if you want to clean up a feature branch and at the same -time rebase it onto ‘master’, then you could use ‘r-iu’. But we -recommend that you instead do that in two steps. First use ‘ri’ to -cleanup the feature branch, and then in a second step ‘ru’ to rebase it -onto ‘master’. That way if things turn out to be more complicated than -you thought and/or you make a mistake and have to start over, then you -only have to redo half the work. - - Explicitly enabling ‘--interactive’ won’t have an effect on the -following commands as they always use that argument anyway, even if it -is not enabled in the transient. - -‘r i’ (‘magit-rebase-interactive’) - This command starts an interactive rebase sequence. - -‘r f’ (‘magit-rebase-autosquash’) - This command combines squash and fixup commits with their intended - targets. - -‘r m’ (‘magit-rebase-edit-commit’) - This command starts an interactive rebase sequence that lets the - user edit a single older commit. - -‘r w’ (‘magit-rebase-reword-commit’) - This command starts an interactive rebase sequence that lets the - user reword a single older commit. - -‘r k’ (‘magit-rebase-remove-commit’) - This command removes a single older commit using rebase. - - When a rebase is in progress, then the transient instead features the -following suffix commands. - -‘r r’ (‘magit-rebase-continue’) - This command restart the current rebasing operation. - - In some cases this pops up a commit message buffer for you do edit. - With a prefix argument the old message is reused as-is. - -‘r s’ (‘magit-rebase-skip’) - This command skips the current commit and restarts the current - rebase operation. - -‘r e’ (‘magit-rebase-edit’) - This command lets the user edit the todo list of the current rebase - operation. - -‘r a’ (‘magit-rebase-abort’) - This command aborts the current rebase operation, restoring the - original branch. - -* Menu: - -* Editing Rebase Sequences:: -* Information About In-Progress Rebase:: - - -File: magit.info, Node: Editing Rebase Sequences, Next: Information About In-Progress Rebase, Up: Rebasing - -6.9.1 Editing Rebase Sequences ------------------------------- - -‘C-c C-c’ (‘with-editor-finish’) - Finish the current editing session by returning with exit code 0. - Git then uses the rebase instructions it finds in the file. - -‘C-c C-k’ (‘with-editor-cancel’) - Cancel the current editing session by returning with exit code 1. - Git then forgoes starting the rebase sequence. - -‘<RET>’ (‘git-rebase-show-commit’) - Show the commit on the current line in another buffer and select - that buffer. - -‘<SPC>’ (‘git-rebase-show-or-scroll-up’) - Show the commit on the current line in another buffer without - selecting that buffer. If the revision buffer is already visible - in another window of the current frame, then instead scroll that - window up. - -‘<DEL>’ (‘git-rebase-show-or-scroll-down’) - Show the commit on the current line in another buffer without - selecting that buffer. If the revision buffer is already visible - in another window of the current frame, then instead scroll that - window down. - -‘p’ (‘git-rebase-backward-line’) - Move to previous line. - -‘n’ (‘forward-line’) - Move to next line. - -‘M-p’ (‘git-rebase-move-line-up’) - Move the current commit (or command) up. - -‘M-n’ (‘git-rebase-move-line-down’) - Move the current commit (or command) down. - -‘r’ (‘git-rebase-reword’) - Edit message of commit on current line. - -‘e’ (‘git-rebase-edit’) - Stop at the commit on the current line. - -‘s’ (‘git-rebase-squash’) - Meld commit on current line into previous commit, and edit message. - -‘f’ (‘git-rebase-fixup’) - Meld commit on current line into previous commit, discarding the - current commit’s message. - -‘k’ (‘git-rebase-kill-line’) - Kill the current action line. - -‘c’ (‘git-rebase-pick’) - Use commit on current line. - -‘x’ (‘git-rebase-exec’) - Insert a shell command to be run after the proceeding commit. - - If there already is such a command on the current line, then edit - that instead. With a prefix argument insert a new command even - when there already is one on the current line. With empty input - remove the command on the current line, if any. - -‘b’ (‘git-rebase-break’) - Insert a break action before the current line, instructing Git to - return control to the user. - -‘y’ (‘git-rebase-insert’) - Read an arbitrary commit and insert it below current line. - -‘C-x u’ (‘git-rebase-undo’) - Undo some previous changes. Like ‘undo’ but works in read-only - buffers. - - -- User Option: git-rebase-auto-advance - Whether to move to next line after changing a line. - - -- User Option: git-rebase-show-instructions - Whether to show usage instructions inside the rebase buffer. - - -- User Option: git-rebase-confirm-cancel - Whether confirmation is required to cancel. - - When a rebase is performed with the ‘--rebase-merges’ option, the -sequence will include a few other types of actions and the following -commands become relevant. - -‘l’ (‘git-rebase-label’) - This commands inserts a label action or edits the one at point. - -‘t’ (‘git-rebase-reset’) - This command inserts a reset action or edits the one at point. The - prompt will offer the labels that are currently present in the - buffer. - -‘MM’ (‘git-rebase-merge’) - The command inserts a merge action or edits the one at point. The - prompt will offer the labels that are currently present in the - buffer. Specifying a message to reuse via ‘-c’ or ‘-C’ is not - supported; an editor will always be invoked for the merge. - -‘Mt’ (‘git-rebase-merge-toggle-editmsg’) - This command toggles between the ‘-C’ and ‘-c’ options of the merge - action at point. These options both specify a commit whose message - should be reused. The lower-case variant instructs Git to invoke - the editor when creating the merge, allowing the user to edit the - message. - - -File: magit.info, Node: Information About In-Progress Rebase, Prev: Editing Rebase Sequences, Up: Rebasing - -6.9.2 Information About In-Progress Rebase ------------------------------------------- - -While a rebase sequence is in progress, the status buffer features a -section that lists the commits that have already been applied as well as -the commits that still have to be applied. - - The commits are split in two halves. When rebase stops at a commit, -either because the user has to deal with a conflict or because s/he -explicitly requested that rebase stops at that commit, then point is -placed on the commit that separates the two groups, i.e., on ‘HEAD’. -The commits above it have not been applied yet, while the ‘HEAD’ and the -commits below it have already been applied. In between these two groups -of applied and yet-to-be applied commits, there sometimes is a commit -which has been dropped. - - Each commit is prefixed with a word and these words are additionally -shown in different colors to indicate the status of the commits. - - The following colors are used: - - • Commits that use the same foreground color as the ‘default’ face - have not been applied yet. - - • Yellow commits have some special relationship to the commit rebase - stopped at. This is used for the words "join", "goal", "same" and - "work" (see below). - - • Gray commits have already been applied. - - • The blue commit is the ‘HEAD’ commit. - - • The green commit is the commit the rebase sequence stopped at. If - this is the same commit as ‘HEAD’ (e.g., because you haven’t done - anything yet after rebase stopped at the commit, then this commit - is shown in blue, not green). There can only be a green *and* a - blue commit at the same time, if you create one or more new commits - after rebase stops at a commit. - - • Red commits have been dropped. They are shown for reference only, - e.g., to make it easier to diff. - - Of course these colors are subject to the color-theme in use. - - The following words are used: - - • Commits prefixed with ‘pick’, ‘reword’, ‘edit’, ‘squash’, and - ‘fixup’ have not been applied yet. These words have the same - meaning here as they do in the buffer used to edit the rebase - sequence. See *note Editing Rebase Sequences::. When the - ‘--rebase-merges’ option was specified, ‘reset’, ‘label’, and - ‘merge’ lines may also be present. - - • Commits prefixed with ‘done’ and ‘onto’ have already been applied. - It is possible for such a commit to be the ‘HEAD’, in which case it - is blue. Otherwise it is grey. - - • The commit prefixed with ‘onto’ is the commit on top of which - all the other commits are being re-applied. This commit - itself did not have to be re-applied, it is the commit rebase - did rewind to before starting to re-apply other commits. - - • Commits prefixed with ‘done’ have already been re-applied. - This includes commits that have been re-applied but also new - commits that you have created during the rebase. - - • All other commits, those not prefixed with any of the above words, - are in some way related to the commit at which rebase stopped. - - To determine whether a commit is related to the stopped-at commit - their hashes, trees and patch-ids (1) are being compared. The - commit message is not used for this purpose. - - Generally speaking commits that are related to the stopped-at - commit can have any of the used colors, though not all color/word - combinations are possible. - - Words used for stopped-at commits are: - - • When a commit is prefixed with ‘void’, then that indicates - that Magit knows for sure that all the changes in that commit - have been applied using several new commits. This commit is - no longer reachable from ‘HEAD’, and it also isn’t one of the - commits that will be applied when resuming the session. - - • When a commit is prefixed with ‘join’, then that indicates - that the rebase sequence stopped at that commit due to a - conflict - you now have to join (merge) the changes with what - has already been applied. In a sense this is the commit - rebase stopped at, but while its effect is already in the - index and in the worktree (with conflict markers), the commit - itself has not actually been applied yet (it isn’t the - ‘HEAD’). So it is shown in yellow, like the other commits - that still have to be applied. - - • When a commit is prefixed with ‘stop’ or a _blue_ or _green_ - ‘same’, then that indicates that rebase stopped at this - commit, that it is still applied or has been applied again, - and that at least its patch-id is unchanged. - - • When a commit is prefixed with ‘stop’, then that - indicates that rebase stopped at that commit because you - requested that earlier, and its patch-id is unchanged. - It might even still be the exact same commit. - - • When a commit is prefixed with a _blue_ or _green_ - ‘same’, then that indicates that while its tree or hash - changed, its patch-id did not. If it is blue, then it is - the ‘HEAD’ commit (as always for blue). When it is - green, then it no longer is ‘HEAD’ because other commit - have been created since (but before continuing the - rebase). - - • When a commit is prefixed with ‘goal’, a _yellow_ ‘same,’ or - ‘work’, then that indicates that rebase applied that commit - but that you then reset ‘HEAD’ to an earlier commit (likely to - split it up into multiple commits), and that there are some - uncommitted changes remaining which likely (but not - necessarily) originate from that commit. - - • When a commit is prefixed with ‘goal’, then that - indicates that it is still possible to create a new - commit with the exact same tree (the "goal") without - manually editing any files, by committing the index, or - by staging all changes and then committing that. This is - the case when the original tree still exists in the index - or worktree in untainted form. - - • When a commit is prefixed with a yellow ‘same’, then that - indicates that it is no longer possible to create a - commit with the exact same tree, but that it is still - possible to create a commit with the same patch-id. This - would be the case if you created a new commit with other - changes, but the changes from the original commit still - exist in the index or working tree in untainted form. - - • When a commit is prefixed with ‘work’, then that - indicates that you reset ‘HEAD’ to an earlier commit, and - that there are some staged and/or unstaged changes - (likely, but not necessarily) originating from that - commit. However it is no longer possible to create a new - commit with the same tree or at least the same patch-id - because you have already made other changes. - - • When a commit is prefixed with ‘poof’ or ‘gone’, then that - indicates that rebase applied that commit but that you then - reset ‘HEAD’ to an earlier commit (likely to split it up into - multiple commits), and that there are no uncommitted changes. - - • When a commit is prefixed with ‘poof’, then that - indicates that it is no longer reachable from ‘HEAD’, but - that it has been replaced with one or more commits, which - together have the exact same effect. - - • When a commit is prefixed with ‘gone’, then that - indicates that it is no longer reachable from ‘HEAD’ and - that we also cannot determine whether its changes are - still in effect in one or more new commits. They might - be, but if so, then there must also be other changes - which makes it impossible to know for sure. - - Do not worry if you do not fully understand the above. That’s okay, -you will acquire a good enough understanding through practice. - - For other sequence operations such as cherry-picking, a similar -section is displayed, but they lack some of the features described -above, due to limitations in the git commands used to implement them. -Most importantly these sequences only support "picking" a commit but not -other actions such as "rewording", and they do not keep track of the -commits which have already been applied. - - ---------- Footnotes ---------- - - (1) The patch-id is a hash of the _changes_ introduced by a commit. -It differs from the hash of the commit itself, which is a hash of the -result of applying that change (i.e., the resulting trees and blobs) as -well as author and committer information, the commit message, and the -hashes of the parents of the commit. The patch-id hash on the other -hand is created only from the added and removed lines, even line numbers -and whitespace changes are ignored when calculating this hash. The -patch-ids of two commits can be used to answer the question "Do these -commits make the same change?". - - -File: magit.info, Node: Cherry Picking, Next: Resetting, Prev: Rebasing, Up: Manipulating - -6.10 Cherry Picking -=================== - -Also see *note (gitman)git-cherry-pick::. - -‘A’ (‘magit-cherry-pick’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - - When no cherry-pick or revert is in progress, then the transient -features the following suffix commands. - -‘A A’ (‘magit-cherry-copy’) - This command copies COMMITS from another branch onto the current - branch. If the region selects multiple commits, then those are - copied, without prompting. Otherwise the user is prompted for a - commit or range, defaulting to the commit at point. - -‘A a’ (‘magit-cherry-apply’) - This command applies the changes in COMMITS from another branch - onto the current branch. If the region selects multiple commits, - then those are used, without prompting. Otherwise the user is - prompted for a commit or range, defaulting to the commit at point. - - This command also has a top-level binding, which can be invoked - without using the transient by typing ‘a’ at the top-level. - - The following commands not only apply some commits to some branch, -but also remove them from some other branch. The removal is performed -using either ‘git-update-ref’ or if necessary ‘git-rebase’. Both -applying commits as well as removing them using ‘git-rebase’ can lead to -conflicts. If that happens, then these commands abort and you not only -have to resolve the conflicts but also finish the process the same way -you would have to if these commands didn’t exist at all. - -‘A h’ (‘magit-cherry-harvest’) - This command moves the selected COMMITS that must be located on - another BRANCH onto the current branch instead, removing them from - the former. When this command succeeds, then the same branch is - current as before. - - Applying the commits on the current branch or removing them from - the other branch can lead to conflicts. When that happens, then - this command stops and you have to resolve the conflicts and then - finish the process manually. - -‘A d’ (‘magit-cherry-donate’) - This command moves the selected COMMITS from the current branch - onto another existing BRANCH, removing them from the former. When - this command succeeds, then the same branch is current as before. - ‘HEAD’ is allowed to be detached initially. - - Applying the commits on the other branch or removing them from the - current branch can lead to conflicts. When that happens, then this - command stops and you have to resolve the conflicts and then finish - the process manually. - -‘A n’ (‘magit-cherry-spinout’) - This command moves the selected COMMITS from the current branch - onto a new branch BRANCH, removing them from the former. When this - command succeeds, then the same branch is current as before. - - Applying the commits on the other branch or removing them from the - current branch can lead to conflicts. When that happens, then this - command stops and you have to resolve the conflicts and then finish - the process manually. - -‘A s’ (‘magit-cherry-spinoff’) - This command moves the selected COMMITS from the current branch - onto a new branch BRANCH, removing them from the former. When this - command succeeds, then the new branch is checked out. - - Applying the commits on the other branch or removing them from the - current branch can lead to conflicts. When that happens, then this - command stops and you have to resolve the conflicts and then finish - the process manually. - - When a cherry-pick or revert is in progress, then the transient -instead features the following suffix commands. - -‘A A’ (‘magit-sequence-continue’) - Resume the current cherry-pick or revert sequence. - -‘A s’ (‘magit-sequence-skip’) - Skip the stopped at commit during a cherry-pick or revert sequence. - -‘A a’ (‘magit-sequence-abort’) - Abort the current cherry-pick or revert sequence. This discards - all changes made since the sequence started. - -* Menu: - -* Reverting:: - - -File: magit.info, Node: Reverting, Up: Cherry Picking - -6.10.1 Reverting ----------------- - -‘V’ (‘magit-revert’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - - When no cherry-pick or revert is in progress, then the transient -features the following suffix commands. - -‘V V’ (‘magit-revert-and-commit’) - Revert a commit by creating a new commit. Prompt for a commit, - defaulting to the commit at point. If the region selects multiple - commits, then revert all of them, without prompting. - -‘V v’ (‘magit-revert-no-commit’) - Revert a commit by applying it in reverse to the working tree. - Prompt for a commit, defaulting to the commit at point. If the - region selects multiple commits, then revert all of them, without - prompting. - - When a cherry-pick or revert is in progress, then the transient -instead features the following suffix commands. - -‘V V’ (‘magit-sequence-continue’) - Resume the current cherry-pick or revert sequence. - -‘V s’ (‘magit-sequence-skip’) - Skip the stopped at commit during a cherry-pick or revert sequence. - -‘V a’ (‘magit-sequence-abort’) - Abort the current cherry-pick or revert sequence. This discards - all changes made since the sequence started. - - -File: magit.info, Node: Resetting, Next: Stashing, Prev: Cherry Picking, Up: Manipulating - -6.11 Resetting -============== - -Also see *note (gitman)git-reset::. - -‘x’ (‘magit-reset-quickly’) - Reset the ‘HEAD’ and index to some commit read from the user and - defaulting to the commit at point, and possibly also reset the - working tree. With a prefix argument reset the working tree - otherwise don’t. - -‘X m’ (‘magit-reset-mixed’) - Reset the ‘HEAD’ and index to some commit read from the user and - defaulting to the commit at point. The working tree is kept as-is. - -‘X s’ (‘magit-reset-soft’) - Reset the ‘HEAD’ to some commit read from the user and defaulting - to the commit at point. The index and the working tree are kept - as-is. - -‘X h’ (‘magit-reset-hard’) - Reset the ‘HEAD’, index, and working tree to some commit read from - the user and defaulting to the commit at point. - -‘X k’ (‘magit-reset-keep’) - Reset the ‘HEAD’, index, and working tree to some commit read from - the user and defaulting to the commit at point. Uncommitted - changes are kept as-is. - -‘X i’ (‘magit-reset-index’) - Reset the index to some commit read from the user and defaulting to - the commit at point. Keep the ‘HEAD’ and working tree as-is, so if - the commit refers to the ‘HEAD’, then this effectively unstages all - changes. - -‘X w’ (‘magit-reset-worktree’) - Reset the working tree to some commit read from the user and - defaulting to the commit at point. Keep the ‘HEAD’ and index - as-is. - -‘X f’ (‘magit-file-checkout’) - Update file in the working tree and index to the contents from a - revision. Both the revision and file are read from the user. - - -File: magit.info, Node: Stashing, Prev: Resetting, Up: Manipulating - -6.12 Stashing -============= - -Also see *note (gitman)git-stash::. - -‘z’ (‘magit-stash’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - -‘z z’ (‘magit-stash-both’) - Create a stash of the index and working tree. Untracked files are - included according to infix arguments. One prefix argument is - equivalent to ‘--include-untracked’ while two prefix arguments are - equivalent to ‘--all’. - -‘z i’ (‘magit-stash-index’) - Create a stash of the index only. Unstaged and untracked changes - are not stashed. - -‘z w’ (‘magit-stash-worktree’) - Create a stash of unstaged changes in the working tree. Untracked - files are included according to infix arguments. One prefix - argument is equivalent to ‘--include-untracked’ while two prefix - arguments are equivalent to ‘--all’. - -‘z x’ (‘magit-stash-keep-index’) - Create a stash of the index and working tree, keeping index intact. - Untracked files are included according to infix arguments. One - prefix argument is equivalent to ‘--include-untracked’ while two - prefix arguments are equivalent to ‘--all’. - -‘z Z’ (‘magit-snapshot-both’) - Create a snapshot of the index and working tree. Untracked files - are included according to infix arguments. One prefix argument is - equivalent to ‘--include-untracked’ while two prefix arguments are - equivalent to ‘--all’. - -‘z I’ (‘magit-snapshot-index’) - Create a snapshot of the index only. Unstaged and untracked - changes are not stashed. - -‘z W’ (‘magit-snapshot-worktree’) - Create a snapshot of unstaged changes in the working tree. - Untracked files are included according to infix arguments. One - prefix argument is equivalent to ‘--include-untracked’ while two - prefix arguments are equivalent to ‘--all’-. - -‘z a’ (‘magit-stash-apply’) - Apply a stash to the working tree. - - First try ‘git stash apply --index’, which tries to preserve the - index stored in the stash, if any. This may fail because applying - the stash could result in conflicts and those have to be stored in - the index, making it impossible to also store the stash’s index - there as well. - - If the above failed, then try ‘git stash apply’. This fails (with - or without ‘--index’) if there are any uncommitted changes to files - that are also modified in the stash. - - If both of the above failed, then apply using ‘git apply’. If - there are no conflicting files, use ‘--3way’. If there are - conflicting files, then using ‘--3way’ requires that those files - are staged first, which may be undesirable, so prompt the user - whether to use ‘--3way’ or ‘--reject’. - - Customize ‘magit-no-confirm’ if you want to always use ‘--3way’, - without being prompted. - -‘z p’ (‘magit-stash-pop’) - Apply a stash to the working tree. On complete success (if the - stash can be applied without any conflicts, and while preserving - the stash’s index) then remove the stash from stash list. - - First try ‘git stash pop --index’, which tries to preserve the - index stored in the stash, if any. This may fail because applying - the stash could result in conflicts and those have to be stored in - the index, making it impossible to also store the stash’s index - there as well. - - If the above failed, then try ‘git stash apply’. This fails (with - or without ‘--index’) if there are any uncommitted changes to files - that are also modified in the stash. - - If both of the above failed, then apply using ‘git apply’. If - there are no conflicting files, use ‘--3way’. If there are - conflicting files, then using ‘--3way’ requires that those files - are staged first, which may be undesirable, so prompt the user - whether to use ‘--3way’ or ‘--reject’. - - Customize ‘magit-no-confirm’ if you want to always use ‘--3way’, - without being prompted. - -‘z k’ (‘magit-stash-drop’) - Remove a stash from the stash list. When the region is active, - offer to drop all contained stashes. - -‘z v’ (‘magit-stash-show’) - Show all diffs of a stash in a buffer. - -‘z b’ (‘magit-stash-branch’) - Create and checkout a new branch from an existing stash. The new - branch starts at the commit that was current when the stash was - created. - -‘z B’ (‘magit-stash-branch-here’) - Create and checkout a new branch from an existing stash. Use the - current branch or ‘HEAD’ as the starting-point of the new branch. - Then apply the stash, dropping it if it applies cleanly. - -‘z f’ (‘magit-stash-format-patch’) - Create a patch from STASH. - -‘k’ (‘magit-stash-clear’) - Remove all stashes saved in REF’s reflog by deleting REF. - -‘z l’ (‘magit-stash-list’) - List all stashes in a buffer. - - -- User Option: magit-stashes-margin - This option specifies whether the margin is initially shown in - stashes buffers and how it is formatted. - - The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. - - • If INIT is non-nil, then the margin is shown initially. - • STYLE controls how to format the author or committer date. It - can be one of ‘age’ (to show the age of the commit), - ‘age-abbreviated’ (to abbreviate the time unit to a - character), or a string (suitable for ‘format-time-string’) to - show the actual date. Option - ‘magit-log-margin-show-committer-date’ controls which date is - being displayed. - • WIDTH controls the width of the margin. This exists for - forward compatibility and currently the value should not be - changed. - • AUTHOR controls whether the name of the author is also shown - by default. - • AUTHOR-WIDTH has to be an integer. When the name of the - author is shown, then this specifies how much space is used to - do so. - - -File: magit.info, Node: Transferring, Next: Miscellaneous, Prev: Manipulating, Up: Top - -7 Transferring -************** - -* Menu: - -* Remotes:: -* Fetching:: -* Pulling:: -* Pushing:: -* Plain Patches:: -* Maildir Patches:: - - -File: magit.info, Node: Remotes, Next: Fetching, Up: Transferring - -7.1 Remotes -=========== - -* Menu: - -* Remote Commands:: -* Remote Git Variables:: - - -File: magit.info, Node: Remote Commands, Next: Remote Git Variables, Up: Remotes - -7.1.1 Remote Commands ---------------------- - -The transient prefix command ‘magit-remote’ is used to add remotes and -to make changes to existing remotes. This command only deals with -remotes themselves, not with branches or the transfer of commits. Those -features are available from separate transient commands. - - Also see *note (gitman)git-remote::. - -‘M’ (‘magit-remote’) - This transient prefix command binds the following suffix commands - and displays them in a temporary buffer until a suffix is invoked. - - By default it also binds and displays the values of some - remote-related Git variables and allows changing their values. - - -- User Option: magit-remote-direct-configure - This option controls whether remote-related Git variables are - accessible directly from the transient ‘magit-remote’. - - If ‘t’ (the default) and a local branch is checked out, then - ‘magit-remote’ features the variables for the upstream remote of - that branch, or if ‘HEAD’ is detached, for ‘origin’, provided that - exists. - - If ‘nil’, then ‘magit-remote-configure’ has to be used to do so. - -‘M C’ (‘magit-remote-configure’) - This transient prefix command binds commands that set the value of - remote-related variables and displays them in a temporary buffer - until the transient is exited. - - With a prefix argument, this command always prompts for a remote. - - Without a prefix argument this depends on whether it was invoked as - a suffix of ‘magit-remote’ and on the - ‘magit-remote-direct-configure’ option. If ‘magit-remote’ already - displays the variables for the upstream, then it does not make - sense to invoke another transient that displays them for the same - remote. In that case this command prompts for a remote. - - The variables are described in *note Remote Git Variables::. - -‘M a’ (‘magit-remote-add’) - This command add a remote and fetches it. The remote name and url - are read in the minibuffer. - -‘M r’ (‘magit-remote-rename’) - This command renames a remote. Both the old and the new names are - read in the minibuffer. - -‘M u’ (‘magit-remote-set-url’) - This command changes the url of a remote. Both the remote and the - new url are read in the minibuffer. - -‘M k’ (‘magit-remote-remove’) - This command deletes a remote, read in the minibuffer. - -‘M p’ (‘magit-remote-prune’) - This command removes stale remote-tracking branches for a remote - read in the minibuffer. - -‘M P’ (‘magit-remote-prune-refspecs’) - This command removes stale refspecs for a remote read in the - minibuffer. - - A refspec is stale if there no longer exists at least one branch on - the remote that would be fetched due to that refspec. A stale - refspec is problematic because its existence causes Git to refuse - to fetch according to the remaining non-stale refspecs. - - If only stale refspecs remain, then this command offers to either - delete the remote or to replace the stale refspecs with the default - refspec ("+refs/heads/*:refs/remotes/REMOTE/*"). - - This command also removes the remote-tracking branches that were - created due to the now stale refspecs. Other stale branches are - not removed. - - -- User Option: magit-remote-add-set-remote.pushDefault - This option controls whether the user is asked whether they want to - set ‘remote.pushDefault’ after adding a remote. - - If ‘ask’, then users is always ask. If ‘ask-if-unset’, then the - user is only if the variable isn’t set already. If ‘nil’, then the - user isn’t asked and the variable isn’t set. If the value is a - string, then the variable is set without the user being asked, - provided that the name of the added remote is equal to that string - and the variable isn’t already set. - - -File: magit.info, Node: Remote Git Variables, Prev: Remote Commands, Up: Remotes - -7.1.2 Remote Git Variables --------------------------- - -These variables can be set from the transient prefix command -‘magit-remote-configure’. By default they can also be set from -‘magit-remote’. See *note Remote Commands::. - - -- Variable: remote.NAME.url - This variable specifies the url of the remote named NAME. It can - have multiple values. - - -- Variable: remote.NAME.fetch - The refspec used when fetching from the remote named NAME. It can - have multiple values. - - -- Variable: remote.NAME.pushurl - This variable specifies the url used for pushing to the remote - named NAME. If it is not specified, then ‘remote.NAME.url’ is used - instead. It can have multiple values. - - -- Variable: remote.NAME.push - The refspec used when pushing to the remote named NAME. It can - have multiple values. - - -- Variable: remote.NAME.tagOpts - This variable specifies what tags are fetched by default. If the - value is ‘--no-tags’ then no tags are fetched. If the value is - ‘--tags’, then all tags are fetched. If this variable has no - value, then only tags are fetched that are reachable from fetched - branches. - - -File: magit.info, Node: Fetching, Next: Pulling, Prev: Remotes, Up: Transferring - -7.2 Fetching -============ - -Also see *note (gitman)git-fetch::. For information about the upstream -and the push-remote, see *note The Two Remotes::. - -‘f’ (‘magit-fetch’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - -‘f p’ (‘magit-fetch-from-pushremote’) - This command fetches from the current push-remote. - - With a prefix argument or when the push-remote is either not - configured or unusable, then let the user first configure the - push-remote. - -‘f u’ (‘magit-fetch-from-upstream’) - This command fetch from the upstream of the current branch. - - If the upstream is configured for the current branch and names an - existing remote, then use that. Otherwise try to use another - remote: If only a single remote is configured, then use that. - Otherwise if a remote named "origin" exists, then use that. - - If no remote can be determined, then this command is not available - from the ‘magit-fetch’ transient prefix and invoking it directly - results in an error. - -‘f e’ (‘magit-fetch-other’) - This command fetch from a repository read from the minibuffer. - -‘f o’ (‘magit-fetch-branch’) - This command fetches a branch from a remote, both of which are read - from the minibuffer. - -‘f r’ (‘magit-fetch-refspec’) - This command fetches from a remote using an explicit refspec, both - of which are read from the minibuffer. - -‘f a’ (‘magit-fetch-all’) - This command fetches from all remotes. - -‘f m’ (‘magit-fetch-modules’) - This command fetches all submodules. With a prefix argument, it - acts as a transient prefix command, allowing the caller to set - options. - - -- User Option: magit-pull-or-fetch - By default fetch and pull commands are available from separate - transient prefix command. Setting this to ‘t’ adds some (but not - all) of the above suffix commands to the ‘magit-pull’ transient. - - If you do that, then you might also want to change the key binding - for these prefix commands, e.g.: - - (setq magit-pull-or-fetch t) - (define-key magit-mode-map "f" 'magit-pull) ; was magit-fetch - (define-key magit-mode-map "F" nil) ; was magit-pull - - -File: magit.info, Node: Pulling, Next: Pushing, Prev: Fetching, Up: Transferring - -7.3 Pulling -=========== - -Also see *note (gitman)git-pull::. For information about the upstream -and the push-remote, see *note The Two Remotes::. - -‘F’ (‘magit-pull’) - This transient prefix command binds the following suffix commands - and displays them in a temporary buffer until a suffix is invoked. - -‘F p’ (‘magit-pull-from-pushremote’) - This command pulls from the push-remote of the current branch. - - With a prefix argument or when the push-remote is either not - configured or unusable, then let the user first configure the - push-remote. - -‘F u’ (‘magit-pull-from-upstream’) - This command pulls from the upstream of the current branch. - - With a prefix argument or when the upstream is either not - configured or unusable, then let the user first configure the - upstream. - -‘F e’ (‘magit-pull-branch’) - This command pulls from a branch read in the minibuffer. - - -File: magit.info, Node: Pushing, Next: Plain Patches, Prev: Pulling, Up: Transferring - -7.4 Pushing -=========== - -Also see *note (gitman)git-push::. For information about the upstream -and the push-remote, see *note The Two Remotes::. - -‘P’ (‘magit-push’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - -‘P p’ (‘magit-push-current-to-pushremote’) - This command pushes the current branch to its push-remote. - - With a prefix argument or when the push-remote is either not - configured or unusable, then let the user first configure the - push-remote. - -‘P u’ (‘magit-push-current-to-upstream’) - This command pushes the current branch to its upstream branch. - - With a prefix argument or when the upstream is either not - configured or unusable, then let the user first configure the - upstream. - -‘P e’ (‘magit-push-current’) - This command pushes the current branch to a branch read in the - minibuffer. - -‘P o’ (‘magit-push-other’) - This command pushes an arbitrary branch or commit somewhere. Both - the source and the target are read in the minibuffer. - -‘P r’ (‘magit-push-refspecs’) - This command pushes one or multiple refspecs to a remote, both of - which are read in the minibuffer. - - To use multiple refspecs, separate them with commas. Completion is - only available for the part before the colon, or when no colon is - used. - -‘P m’ (‘magit-push-matching’) - This command pushes all matching branches to another repository. - - If only one remote exists, then push to that. Otherwise prompt for - a remote, offering the remote configured for the current branch as - default. - -‘P t’ (‘magit-push-tags’) - This command pushes all tags to another repository. - - If only one remote exists, then push to that. Otherwise prompt for - a remote, offering the remote configured for the current branch as - default. - -‘P T’ (‘magit-push-tag’) - This command pushes a tag to another repository. - - One of the infix arguments, ‘--force-with-lease’, deserves a word of -caution. It is passed without a value, which means "permit a force push -as long as the remote-tracking branches match their counterparts on the -remote end". If you’ve set up a tool to do automatic fetches (Magit -itself does not provide such functionality), using ‘--force-with-lease’ -can be dangerous because you don’t actually control or know the state of -the remote-tracking refs. In that case, you should consider setting -‘push.useForceIfIncludes’ to ‘true’ (available since Git 2.30). - - Two more push commands exist, which by default are not available from -the push transient. See their doc-strings for instructions on how to -add them to the transient. - - -- Command: magit-push-implicitly args - This command pushes somewhere without using an explicit refspec. - - This command simply runs ‘git push -v [ARGS]’. ARGS are the infix - arguments. No explicit refspec arguments are used. Instead the - behavior depends on at least these Git variables: ‘push.default’, - ‘remote.pushDefault’, ‘branch.<branch>.pushRemote’, - ‘branch.<branch>.remote’, ‘branch.<branch>.merge’, and - ‘remote.<remote>.push’. - - If you add this suffix to a transient prefix without explicitly - specifying the description, then an attempt is made to predict what - this command will do. For example: - - (transient-insert-suffix 'magit-push \"p\" - '(\"i\" magit-push-implicitly))" - - -- Command: magit-push-to-remote remote args - This command pushes to the remote REMOTE without using an explicit - refspec. The remote is read in the minibuffer. - - This command simply runs ‘git push -v [ARGS] REMOTE’. ARGS are the - infix arguments. No refspec arguments are used. Instead the - behavior depends on at least these Git variables: ‘push.default’, - ‘remote.pushDefault’, ‘branch.<branch>.pushRemote’, - ‘branch.<branch>.remote’, ‘branch.<branch>.merge’, and - ‘remote.<remote>.push’. - - -File: magit.info, Node: Plain Patches, Next: Maildir Patches, Prev: Pushing, Up: Transferring - -7.5 Plain Patches -================= - -‘W’ (‘magit-patch’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - -‘W c’ (‘magit-patch-create’) - This command creates patches for a set commits. If the region - marks several commits, then it creates patches for all of them. - Otherwise it functions as a transient prefix command, which - features several infix arguments and binds itself as a suffix - command. When this command is invoked as a suffix of itself, then - it creates a patch using the specified infix arguments. - -‘w a’ (‘magit-patch-apply’) - This command applies a patch. This is a transient prefix command, - which features several infix arguments and binds itself as a suffix - command. When this command is invoked as a suffix of itself, then - it applies a patch using the specified infix arguments. - -‘W s’ (‘magit-patch-save’) - This command creates a patch from the current diff. - - Inside ‘magit-diff-mode’ or ‘magit-revision-mode’ buffers, ‘C-x - C-w’ is also bound to this command. - - It is also possible to save a plain patch file by using ‘C-x C-w’ -inside a ‘magit-diff-mode’ or ‘magit-revision-mode’ buffer. - - -File: magit.info, Node: Maildir Patches, Prev: Plain Patches, Up: Transferring - -7.6 Maildir Patches -=================== - -Also see *note (gitman)git-am::. and *note (gitman)git-apply::. - -‘w’ (‘magit-am’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - -‘w w’ (‘magit-am-apply-patches’) - This command applies one or more patches. If the region marks - files, then those are applied as patches. Otherwise this command - reads a file-name in the minibuffer, defaulting to the file at - point. - -‘w m’ (‘magit-am-apply-maildir’) - This command applies patches from a maildir. - -‘w a’ (‘magit-patch-apply’) - This command applies a plain patch. For a longer description see - *note Plain Patches::. This command is only available from the - ‘magit-am’ transient for historic reasons. - - When an "am" operation is in progress, then the transient instead -features the following suffix commands. - -‘w w’ (‘magit-am-continue’) - This command resumes the current patch applying sequence. - -‘w s’ (‘magit-am-skip’) - This command skips the stopped at patch during a patch applying - sequence. - -‘w a’ (‘magit-am-abort’) - This command aborts the current patch applying sequence. This - discards all changes made since the sequence started. - - -File: magit.info, Node: Miscellaneous, Next: Customizing, Prev: Transferring, Up: Top - -8 Miscellaneous -*************** - -* Menu: - -* Tagging:: -* Notes:: -* Submodules:: -* Subtree:: -* Worktree:: -* Sparse checkouts:: -* Bundle:: -* Common Commands:: -* Wip Modes:: -* Commands for Buffers Visiting Files:: -* Minor Mode for Buffers Visiting Blobs:: - - -File: magit.info, Node: Tagging, Next: Notes, Up: Miscellaneous - -8.1 Tagging -=========== - -Also see *note (gitman)git-tag::. - -‘t’ (‘magit-tag’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - -‘t t’ (‘magit-tag-create’) - This command creates a new tag with the given NAME at REV. With a - prefix argument it creates an annotated tag. - -‘t r’ (‘magit-tag-release’) - This commands creates a release tag. It assumes that release tags - match ‘magit-release-tag-regexp’. - - First it prompts for the name of the new tag using the highest - existing tag as initial input and leaving it to the user to - increment the desired part of the version string. If you use - unconventional release tags or version numbers (e.g., - ‘v1.2.3-custom.1’), you can set the ‘magit-release-tag-regexp’ and - ‘magit-tag-version-regexp-alist’ variables. - - If ‘--annotate’ is enabled then it prompts for the message of the - new tag. The proposed tag message is based on the message of the - highest tag, provided that that contains the corresponding version - string and substituting the new version string for that. Otherwise - it proposes something like "Foo-Bar 1.2.3", given, for example, a - TAG "v1.2.3" and a repository located at something like - "/path/to/foo-bar". - -‘t k’ (‘magit-tag-delete’) - This command deletes one or more tags. If the region marks - multiple tags (and nothing else), then it offers to delete those. - Otherwise, it prompts for a single tag to be deleted, defaulting to - the tag at point. - -‘t p’ (‘magit-tag-prune’) - This command offers to delete tags missing locally from REMOTE, and - vice versa. - - -File: magit.info, Node: Notes, Next: Submodules, Prev: Tagging, Up: Miscellaneous - -8.2 Notes -========= - -Also see *note (gitman)git-notes::. - -‘T’ (‘magit-notes’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - -‘T T’ (‘magit-notes-edit’) - Edit the note attached to a commit, defaulting to the commit at - point. - - By default use the value of Git variable ‘core.notesRef’ or - "refs/notes/commits" if that is undefined. - -‘T r’ (‘magit-notes-remove’) - Remove the note attached to a commit, defaulting to the commit at - point. - - By default use the value of Git variable ‘core.notesRef’ or - "refs/notes/commits" if that is undefined. - -‘T p’ (‘magit-notes-prune’) - Remove notes about unreachable commits. - - It is possible to merge one note ref into another. That may result -in conflicts which have to resolved in the temporary worktree -".git/NOTES_MERGE_WORKTREE". - -‘T m’ (‘magit-notes-merge’) - Merge the notes of a ref read from the user into the current notes - ref. The current notes ref is the value of Git variable - ‘core.notesRef’ or "refs/notes/commits" if that is undefined. - - When a notes merge is in progress then the transient features the -following suffix commands, instead of those listed above. - -‘T c’ (‘magit-notes-merge-commit’) - Commit the current notes ref merge, after manually resolving - conflicts. - -‘T a’ (‘magit-notes-merge-abort’) - Abort the current notes ref merge. - - The following variables control what notes reference ‘magit-notes-*’, -‘git notes’ and ‘git show’ act on and display. Both the local and -global values are displayed and can be modified. - - -- Variable: core.notesRef - This variable specifies the notes ref that is displayed by default - and which commands act on by default. - - -- Variable: notes.displayRef - This variable specifies additional notes ref to be displayed in - addition to the ref specified by ‘core.notesRef’. It can have - multiple values and may end with ‘*’ to display all refs in the - ‘refs/notes/’ namespace (or ‘**’ if some names contain slashes). - - -File: magit.info, Node: Submodules, Next: Subtree, Prev: Notes, Up: Miscellaneous - -8.3 Submodules -============== - -Also see *note (gitman)git-submodule::. - -* Menu: - -* Listing Submodules:: -* Submodule Transient:: - - -File: magit.info, Node: Listing Submodules, Next: Submodule Transient, Up: Submodules - -8.3.1 Listing Submodules ------------------------- - -The command ‘magit-list-submodules’ displays a list of the current -repository’s submodules in a separate buffer. It’s also possible to -display information about submodules directly in the status buffer of -the super-repository by adding ‘magit-insert-modules’ to the hook -‘magit-status-sections-hook’ as described in *note Status Module -Sections::. - - -- Command: magit-list-submodules - This command displays a list of the current repository’s populated - submodules in a separate buffer. - - It can be invoked by pressing ‘RET’ on the section titled - "Modules". - - -- User Option: magit-submodule-list-columns - This option controls what columns are displayed by the command - ‘magit-list-submodules’ and how they are displayed. - - Each element has the form ‘(HEADER WIDTH FORMAT PROPS)’. - - HEADER is the string displayed in the header. WIDTH is the width - of the column. FORMAT is a function that is called with one - argument, the repository identification (usually its basename), and - with ‘default-directory’ bound to the toplevel of its working tree. - It has to return a string to be inserted or nil. PROPS is an alist - that supports the keys ‘:right-align’, ‘:pad-right’ and ‘:sort’. - - The ‘:sort’ function has a weird interface described in the - docstring of ‘tabulated-list--get-sort’. Alternatively ‘<’ and - ‘magit-repolist-version<’ can be used as those functions are - automatically replaced with functions that satisfy the interface. - Set ‘:sort’ to ‘nil’ to inhibit sorting; if unspecified, then the - column is sortable using the default sorter. - - You may wish to display a range of numeric columns using just one - character per column and without any padding between columns, in - which case you should use an appropriate HEADER, set WIDTH to 1, - and set ‘:pad-right’ to 9. ‘+’ is substituted for numbers higher - than 9. - - -File: magit.info, Node: Submodule Transient, Prev: Listing Submodules, Up: Submodules - -8.3.2 Submodule Transient -------------------------- - -‘o’ (‘magit-submodule’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - - Some of the below commands default to act on the modules that are -selected using the region. For brevity their description talk about -"the selected modules", but if no modules are selected, then they act on -the current module instead, or if point isn’t on a module, then the read -a single module to act on. With a prefix argument these commands ignore -the selection and the current module and instead act on all suitable -modules. - -‘o a’ (‘magit-submodule-add’) - This commands adds the repository at URL as a module. Optional - PATH is the path to the module relative to the root of the - super-project. If it is nil then the path is determined based on - URL. - -‘o r’ (‘magit-submodule-register’) - This command registers the selected modules by copying their urls - from ".gitmodules" to "$GIT_DIR/config". These values can then be - edited before running ‘magit-submodule-populate’. If you don’t - need to edit any urls, then use the latter directly. - -‘o p’ (‘magit-submodule-populate’) - This command creates the working directory or directories of the - selected modules, checking out the recorded commits. - -‘o u’ (‘magit-submodule-update’) - This command updates the selected modules checking out the recorded - commits. - -‘o s’ (‘magit-submodule-synchronize’) - This command synchronizes the urls of the selected modules, copying - the values from ".gitmodules" to the ".git/config" of the - super-project as well those of the modules. - -‘o d’ (‘magit-submodule-unpopulate’) - This command removes the working directory of the selected modules. - -‘o l’ (‘magit-list-submodules’) - This command displays a list of the current repository’s modules. - -‘o f’ (‘magit-fetch-modules’) - This command fetches all populated modules. With a prefix - argument, it acts as a transient prefix command, allowing the - caller to set options. - - Also fetch the super-repository, because ‘git fetch’ does not - support not doing that. - - -File: magit.info, Node: Subtree, Next: Worktree, Prev: Submodules, Up: Miscellaneous - -8.4 Subtree -=========== - -Also see *note (gitman)git-subtree::. - -‘O’ (‘magit-subtree’) - This transient prefix command binds the two sub-transients; one for - importing a subtree and one for exporting a subtree. - -‘O i’ (‘magit-subtree-import’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - - The suffixes of this command import subtrees. - - If the ‘--prefix’ argument is set, then the suffix commands use - that prefix without prompting the user. If it is unset, then they - read the prefix in the minibuffer. - -‘O i a’ (‘magit-subtree-add’) - This command adds COMMIT from REPOSITORY as a new subtree at - PREFIX. - -‘O i c’ (‘magit-subtree-add-commit’) - This command add COMMIT as a new subtree at PREFIX. - -‘O i m’ (‘magit-subtree-merge’) - This command merges COMMIT into the PREFIX subtree. - -‘O i f’ (‘magit-subtree-pull’) - This command pulls COMMIT from REPOSITORY into the PREFIX subtree. - -‘O e’ (‘magit-subtree-export’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - - The suffixes of this command export subtrees. - - If the ‘--prefix’ argument is set, then the suffix commands use - that prefix without prompting the user. If it is unset, then they - read the prefix in the minibuffer. - -‘O e p’ (‘magit-subtree-push’) - This command extract the history of the subtree PREFIX and pushes - it to REF on REPOSITORY. - -‘O e s’ (‘magit-subtree-split’) - This command extracts the history of the subtree PREFIX. - - -File: magit.info, Node: Worktree, Next: Sparse checkouts, Prev: Subtree, Up: Miscellaneous - -8.5 Worktree -============ - -Also see *note (gitman)git-worktree::. - -‘Z’ (‘magit-worktree’) - This transient prefix command binds the following suffix commands - and displays them in a temporary buffer until a suffix is invoked. - -‘Z b’ (‘magit-worktree-checkout’) - Checkout BRANCH in a new worktree at PATH. - -‘Z c’ (‘magit-worktree-branch’) - Create a new BRANCH and check it out in a new worktree at PATH. - -‘Z m’ (‘magit-worktree-move’) - Move an existing worktree to a new PATH. - -‘Z k’ (‘magit-worktree-delete’) - Delete a worktree, defaulting to the worktree at point. The - primary worktree cannot be deleted. - -‘Z g’ (‘magit-worktree-status’) - Show the status for the worktree at point. - - If there is no worktree at point, then read one in the minibuffer. - If the worktree at point is the one whose status is already being - displayed in the current buffer, then show it in Dired instead. - - -File: magit.info, Node: Sparse checkouts, Next: Bundle, Prev: Worktree, Up: Miscellaneous - -8.6 Sparse checkouts -==================== - -Sparse checkouts provide a way to restrict the working tree to a subset -of directories. See *note (gitman)git-sparse-checkout::. - - *Warning*: Git introduced the ‘git sparse-checkout’ command in -version 2.25 and still advertises it as experimental and subject to -change. Magit’s interface should be considered the same. In -particular, if Git introduces a backward incompatible change, Magit’s -sparse checkout functionality may be updated in a way that requires a -more recent Git version. - -‘>’ (‘magit-sparse-checkout’) - This transient prefix command binds the following suffix commands - and displays them in a temporary buffer until a suffix is invoked. - -‘> e’ (‘magit-sparse-checkout-enable’) - This command initializes a sparse checkout that includes only the - files in the top-level directory. - - Note that ‘magit-sparse-checkout-set’ and - ‘magit-sparse-checkout-add’ automatically initialize a sparse - checkout if necessary. However, you may want to call - ‘magit-sparse-checkout-enable’ explicitly to re-initialize a sparse - checkout after calling ‘magit-sparse-checkout-disable’, to pass - additional arguments to ‘git sparse-checkout init’, or to execute - the initialization asynchronously. - -‘> s’ (‘magit-sparse-checkout-set’) - This command takes a list of directories and configures the sparse - checkout to include only files in those subdirectories. Any - previously included directories are excluded unless they are in the - provided list of directories. - -‘> a’ (‘magit-sparse-checkout-add’) - This command is like ‘magit-sparse-checkout-set’, but instead adds - the specified list of directories to the set of directories that is - already included in the sparse checkout. - -‘> r’ (‘magit-sparse-checkout-reapply’) - This command applies the currently configured sparse checkout - patterns to the working tree. This is useful to call if excluded - files have been checked out after operations such as merging or - rebasing. - -‘> d’ (‘magit-sparse-checkout-disable’) - This command restores the full checkout. To return to the previous - sparse checkout, call ‘magit-sparse-checkout-enable’. - - A sparse checkout can also be initiated when cloning a repository by -using the ‘magit-clone-sparse’ command in the ‘magit-clone’ transient -(see *note Cloning Repository::). - - If you want the status buffer to indicate when a sparse checkout is -enabled, add the function ‘magit-sparse-checkout-insert-header’ to -‘magit-status-headers-hook’. - - -File: magit.info, Node: Bundle, Next: Common Commands, Prev: Sparse checkouts, Up: Miscellaneous - -8.7 Bundle -========== - -Also see *note (gitman)git-bundle::. - - -- Command: magit-bundle - This transient prefix command binds several suffix commands for - running ‘git bundle’ subcommands and displays them in a temporary - buffer until a suffix is invoked. - - -File: magit.info, Node: Common Commands, Next: Wip Modes, Prev: Bundle, Up: Miscellaneous - -8.8 Common Commands -=================== - - -- Command: magit-switch-to-repository-buffer - -- Command: magit-switch-to-repository-buffer-other-window - -- Command: magit-switch-to-repository-buffer-other-frame - -- Command: magit-display-repository-buffer - These commands read any existing Magit buffer that belongs to the - current repository from the user and then switch to the selected - buffer (without refreshing it). - - The last variant uses ‘magit-display-buffer’ to do so and thus - respects ‘magit-display-buffer-function’. - - These are some of the commands that can be used in all buffers whose -major-modes derive from ‘magit-mode’. There are other common commands -beside the ones below, but these didn’t fit well anywhere else. - -‘C-w’ (‘magit-copy-section-value’) - This command saves the value of the current section to the - ‘kill-ring’, and, provided that the current section is a commit, - branch, or tag section, it also pushes the (referenced) revision to - the ‘magit-revision-stack’. - - When the current section is a branch or a tag, and a prefix - argument is used, then it saves the revision at its tip to the - ‘kill-ring’ instead of the reference name. - - When the region is active, this command saves that to the - ‘kill-ring’, like ‘kill-ring-save’ would, instead of behaving as - described above. If a prefix argument is used and the region is - within a hunk, then it strips the diff marker column and keeps only - either the added or removed lines, depending on the sign of the - prefix argument. - -‘M-w’ (‘magit-copy-buffer-revision’) - This command saves the revision being displayed in the current - buffer to the ‘kill-ring’ and also pushes it to the - ‘magit-revision-stack’. It is mainly intended for use in - ‘magit-revision-mode’ buffers, the only buffers where it is always - unambiguous exactly which revision should be saved. - - Most other Magit buffers usually show more than one revision, in - some way or another, so this command has to select one of them, and - that choice might not always be the one you think would have been - the best pick. - - Outside of Magit ‘M-w’ and ‘C-w’ are usually bound to -‘kill-ring-save’ and ‘kill-region’, and these commands would also be -useful in Magit buffers. Therefore when the region is active, then both -of these commands behave like ‘kill-ring-save’ instead of as described -above. - - -File: magit.info, Node: Wip Modes, Next: Commands for Buffers Visiting Files, Prev: Common Commands, Up: Miscellaneous - -8.9 Wip Modes -============= - -Git keeps *committed* changes around long enough for users to recover -changes they have accidentally deleted. It does so by not garbage -collecting any committed but no longer referenced objects for a certain -period of time, by default 30 days. - - But Git does *not* keep track of *uncommitted* changes in the working -tree and not even the index (the staging area). Because Magit makes it -so convenient to modify uncommitted changes, it also makes it easy to -shoot yourself in the foot in the process. - - For that reason Magit provides a global mode that saves *tracked* -files to work-in-progress references after or before certain actions. -(At present untracked files are never saved and for technical reasons -nothing is saved before the first commit has been created). - - Two separate work-in-progress references are used to track the state -of the index and of the working tree: ‘refs/wip/index/<branchref>’ and -‘refs/wip/wtree/<branchref>’, where ‘<branchref>’ is the full ref of the -current branch, e.g., ‘refs/heads/master’. When the ‘HEAD’ is detached -then ‘HEAD’ is used in place of ‘<branchref>’. - - Checking out another branch (or detaching ‘HEAD’) causes the use of -different wip refs for subsequent changes. - - -- User Option: magit-wip-mode - When this mode is enabled, then uncommitted changes are committed - to dedicated work-in-progress refs whenever appropriate (i.e., when - dataloss would be a possibility otherwise). - - Setting this variable directly does not take effect; either use the - Custom interface to do so or call the respective mode function. - - For historic reasons this mode is implemented on top of four other - ‘magit-wip-*’ modes, which can also be used individually, if you - want finer control over when the wip refs are updated; but that is - discouraged. See *note Legacy Wip Modes::. - - To view the log for a branch and its wip refs use the commands -‘magit-wip-log’ and ‘magit-wip-log-current’. You should use ‘--graph’ -when using these commands. - - -- Command: magit-wip-log - This command shows the log for a branch and its wip refs. With a - negative prefix argument only the worktree wip ref is shown. - - The absolute numeric value of the prefix argument controls how many - "branches" of each wip ref are shown. This is only relevant if the - value of ‘magit-wip-merge-branch’ is ‘nil’. - - -- Command: magit-wip-log-current - This command shows the log for the current branch and its wip refs. - With a negative prefix argument only the worktree wip ref is shown. - - The absolute numeric value of the prefix argument controls how many - "branches" of each wip ref are shown. This is only relevant if the - value of ‘magit-wip-merge-branch’ is ‘nil’. - -‘X w’ (‘magit-reset-worktree’) - This command resets the working tree to some commit read from the - user and defaulting to the commit at point, while keeping the - ‘HEAD’ and index as-is. - - This can be used to restore files to the state committed to a wip - ref. Note that this will discard any unstaged changes that might - have existed before invoking this command (but of course only after - committing that to the working tree wip ref). - - Note that even if you enable ‘magit-wip-mode’ this won’t give you -perfect protection. The most likely scenario for losing changes despite -the use of ‘magit-wip-mode’ is making a change outside Emacs and then -destroying it also outside Emacs. In some such a scenario, Magit, being -an Emacs package, didn’t get the opportunity to keep you from shooting -yourself in the foot. - - When you are unsure whether Magit did commit a change to the wip -refs, then you can explicitly request that all changes to all tracked -files are being committed. - -‘M-x magit-wip-commit’ - This command commits all changes to all tracked files to the index - and working tree work-in-progress refs. Like the modes described - above, it does not commit untracked files, but it does check all - tracked files for changes. Use this command when you suspect that - the modes might have overlooked a change made outside Emacs/Magit. - - -- User Option: magit-wip-namespace - The namespace used for work-in-progress refs. It has to end with a - slash. The wip refs are named ‘<namespace>index/<branchref>’ and - ‘<namespace>wtree/<branchref>’. When snapshots are created while - the ‘HEAD’ is detached then ‘HEAD’ is used in place of - ‘<branchref>’. - - -- User Option: magit-wip-mode-lighter - Mode-line lighter for ‘magit-wip--mode’. - -* Menu: - -* Wip Graph:: -* Legacy Wip Modes:: - - -File: magit.info, Node: Wip Graph, Next: Legacy Wip Modes, Up: Wip Modes - -8.9.1 Wip Graph ---------------- - - -- User Option: magit-wip-merge-branch - This option controls whether the current branch is merged into the - wip refs after a new commit was created on the branch. - - If non-nil and the current branch has new commits, then it is - merged into the wip ref before creating a new wip commit. This - makes it easier to inspect wip history and the wip commits are - never garbage collected. - - If nil and the current branch has new commits, then the wip ref is - reset to the tip of the branch before creating a new wip commit. - With this setting wip commits are eventually garbage collected. - - When ‘magit-wip-merge-branch’ is ‘t’, then the history looks like -this: - - *--*--*--*--*--* refs/wip/index/refs/heads/master - / / / - A-----B-----C refs/heads/master - - When ‘magit-wip-merge-branch’ is ‘nil’, then creating a commit on the -real branch and then making a change causes the wip refs to be recreated -to fork from the new commit. But the old commits on the wip refs are -not lost. They are still available from the reflog. To make it easier -to see when the fork point of a wip ref was changed, an additional -commit with the message "restart autosaving" is created on it (‘xxO’ -commits below are such boundary commits). - - Starting with - - BI0---BI1 refs/wip/index/refs/heads/master - / - A---B refs/heads/master - \ - BW0---BW1 refs/wip/wtree/refs/heads/master - - and committing the staged changes and editing and saving a file would -result in - - BI0---BI1 refs/wip/index/refs/heads/master - / - A---B---C refs/heads/master - \ \ - \ CW0---CW1 refs/wip/wtree/refs/heads/master - \ - BW0---BW1 refs/wip/wtree/refs/heads/master@{2} - - The fork-point of the index wip ref is not changed until some change -is being staged. Likewise just checking out a branch or creating a -commit does not change the fork-point of the working tree wip ref. The -fork-points are not adjusted until there actually is a change that -should be committed to the respective wip ref. - - -File: magit.info, Node: Legacy Wip Modes, Prev: Wip Graph, Up: Wip Modes - -8.9.2 Legacy Wip Modes ----------------------- - -It is recommended that you use the mode ‘magit-wip-mode’ (which see) and -ignore the existence of the following modes, which are preserved for -historic reasons. - - Setting the following variables directly does not take effect; either -use the Custom interface to do so or call the respective mode functions. - - -- User Option: magit-wip-after-save-mode - When this mode is enabled, then saving a buffer that visits a file - tracked in a Git repository causes its current state to be - committed to the working tree wip ref for the current branch. - - -- User Option: magit-wip-after-apply-mode - When this mode is enabled, then applying (i.e., staging, unstaging, - discarding, reversing, and regularly applying) a change to a file - tracked in a Git repository causes its current state to be - committed to the index and/or working tree wip refs for the current - branch. - - If you only ever edit files using Emacs and only ever interact with -Git using Magit, then the above two modes should be enough to protect -each and every change from accidental loss. In practice nobody does -that. Two additional modes exists that do commit to the wip refs before -making changes that could cause the loss of earlier changes. - - -- User Option: magit-wip-before-change-mode - When this mode is enabled, then certain commands commit the - existing changes to the files they are about to make changes to. - - -- User Option: magit-wip-initial-backup-mode - When this mode is enabled, then the current version of a file is - committed to the worktree wip ref before the buffer visiting that - file is saved for the first time since the buffer was created. - - This backs up the same version of the file that ‘backup-buffer’ - would save. While ‘backup-buffer’ uses a backup file, this mode - uses the same worktree wip ref as used by the other Magit Wip - modes. Like ‘backup-buffer’, it only does this once; unless you - kill the buffer and visit the file again only one backup will be - created per Emacs session. - - This mode ignores the variables that affect ‘backup-buffer’ and can - be used along-side that function, which is recommended because it - only backs up files that are tracked in a Git repository. - - -- User Option: magit-wip-after-save-local-mode-lighter - Mode-line lighter for ‘magit-wip-after-save-local-mode’. - - -- User Option: magit-wip-after-apply-mode-lighter - Mode-line lighter for ‘magit-wip-after-apply-mode’. - - -- User Option: magit-wip-before-change-mode-lighter - Mode-line lighter for ‘magit-wip-before-change-mode’. - - -- User Option: magit-wip-initial-backup-mode-lighter - Mode-line lighter for ‘magit-wip-initial-backup-mode’. - - -File: magit.info, Node: Commands for Buffers Visiting Files, Next: Minor Mode for Buffers Visiting Blobs, Prev: Wip Modes, Up: Miscellaneous - -8.10 Commands for Buffers Visiting Files -======================================== - -By default Magit defines a few global key bindings. These bindings are -a compromise between providing no bindings at all and providing the -better bindings I would have liked to use instead. Magit cannot provide -the set of recommended bindings by default because those key sequences -are strictly reserved for bindings added by the user. Also see *note -Global Bindings:: and *note (elisp)Key Binding Conventions::. - - To use the recommended bindings, add this to your init file and -restart Emacs. - - (setq magit-define-global-key-bindings 'recommended) - - If you don’t want Magit to add any bindings to the global keymap at -all, add this to your init file and restart Emacs. - - (setq magit-define-global-key-bindings nil) - -‘C-c f’ (‘magit-file-dispatch’) -‘C-c f s’ (‘magit-stage-file’) -‘C-c f s’ (‘magit-stage-buffer-file’) -‘C-c f u’ (‘magit-unstage-file’) -‘C-c f u’ (‘magit-unstage-buffer-file’) -‘C-c f , x’ (‘magit-file-untrack’) -‘C-c f , r’ (‘magit-file-rename’) -‘C-c f , k’ (‘magit-file-delete’) -‘C-c f , c’ (‘magit-file-checkout’) -‘C-c f D’ (‘magit-diff’) -‘C-c f d’ (‘magit-diff-buffer-file’) -‘C-c f L’ (‘magit-log’) -‘C-c f l’ (‘magit-log-buffer-file’) -‘C-c f t’ (‘magit-log-trace-definition’) -‘C-c f M’ (‘magit-log-merged’) -‘C-c f B’ (‘magit-blame’) -‘C-c f b’ (‘magit-blame-additions’) -‘C-c f r’ (‘magit-blame-removal’) -‘C-c f f’ (‘magit-blame-reverse’) -‘C-c f m’ (‘magit-blame-echo’) -‘C-c f q’ (‘magit-blame-quit’) -‘C-c f p’ (‘magit-blob-previous’) -‘C-c f n’ (‘magit-blob-next’) -‘C-c f v’ (‘magit-find-file’) -‘C-c f V’ (‘magit-blob-visit-file’) -‘C-c f g’ (‘magit-status-here’) -‘C-c f G’ (‘magit-display-repository-buffer’) -‘C-c f c’ (‘magit-commit’) -‘C-c f e’ (‘magit-edit-line-commit’) - Each of these commands is documented individually right below, - alongside their default key bindings. The bindings shown above are - the recommended bindings, which you can enable by following the - instructions further up. - -‘C-c M-g’ (‘magit-file-dispatch’) - This transient prefix command binds the following suffix commands - and displays them in a temporary buffer until a suffix is invoked. - -‘C-c M-g s’ (‘magit-stage-file’) -‘C-c M-g s’ (‘magit-stage-buffer-file’) - Stage all changes to the file being visited in the current buffer. - When not visiting a file, then the first command is used, which - prompts for a file. - -‘C-c M-g u’ (‘magit-unstage-file’) -‘C-c M-g u’ (‘magit-unstage-buffer-file’) - Unstage all changes to the file being visited in the current - buffer. When not visiting a file, then the first command is used, - which prompts for a file. - -‘C-c M-g , x’ (‘magit-file-untrack’) - This command untracks a file read from the user, defaulting to the - visited file. - -‘C-c M-g , r’ (‘magit-file-rename’) - This command renames a file read from the user, defaulting to the - visited file. - -‘C-c M-g , k’ (‘magit-file-delete’) - This command deletes a file read from the user, defaulting to the - visited file. - -‘C-c M-g , c’ (‘magit-file-checkout’) - This command updates a file in the working tree and index to the - contents from a revision. Both the revision and file are read from - the user. - -‘C-c M-g D’ (‘magit-diff’) - This transient prefix command binds several diff suffix commands - and infix arguments and displays them in a temporary buffer until a - suffix is invoked. See *note Diffing::. - - This is the same command that ‘d’ is bound to in Magit buffers. If - this command is invoked from a file-visiting buffer, then the - initial value of the option (‘--’) that limits the diff to certain - file(s) is set to the visited file. - -‘C-c M-g d’ (‘magit-diff-buffer-file’) - This command shows the diff for the file of blob that the current - buffer visits. - - -- User Option: magit-diff-buffer-file-locked - This option controls whether ‘magit-diff-buffer-file’ uses a - dedicated buffer. See *note Modes and Buffers::. - -‘C-c M-g L’ (‘magit-log’) - This transient prefix command binds several log suffix commands and - infix arguments and displays them in a temporary buffer until a - suffix is invoked. See *note Logging::. - - This is the same command that ‘l’ is bound to in Magit buffers. If - this command is invoked from a file-visiting buffer, then the - initial value of the option (‘--’) that limits the log to certain - file(s) is set to the visited file. - -‘C-c M-g l’ (‘magit-log-buffer-file’) - This command shows the log for the file of blob that the current - buffer visits. Renames are followed when a prefix argument is used - or when ‘--follow’ is an active log argument. When the region is - active, the log is restricted to the selected line range. - - -- User Option: magit-log-buffer-file-locked - This option controls whether ‘magit-log-buffer-file’ uses a - dedicated buffer. See *note Modes and Buffers::. - -‘C-c M-g t’ (‘magit-log-trace-definition’) - This command shows the log for the definition at point. - -‘C-c M-g M’ (‘magit-log-merged’) - This command reads a commit and a branch in shows a log concerning - the merge of the former into the latter. This shows multiple - commits even in case of a fast-forward merge. - -‘C-c M-g B’ (‘magit-blame’) - This transient prefix command binds all blaming suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. - - For more information about this and the following commands also see - *note Blaming::. - - In addition to the ‘magit-blame’ sub-transient, the dispatch - transient also binds several blaming suffix commands directly. See - *note Blaming:: for information about those commands and bindings. - -‘C-c M-g p’ (‘magit-blob-previous’) - This command visits the previous blob which modified the current - file. - -‘C-c M-g n’ (‘magit-blob-next’) - This command visits the next blob which modified the current file. - -‘C-c M-g v’ (‘magit-find-file’) - This command reads a revision and file and visits the respective - blob. - -‘C-c M-g V’ (‘magit-blob-visit-file’) - This command visits the file from the working tree, corresponding - to the current blob. When visiting a blob or the version from the - index, then it goes to the same location in the respective file in - the working tree. - -‘C-c M-g g’ (‘magit-status-here’) - This command displays the status of the current repository in a - buffer, like ‘magit-status’ does. Additionally it tries to go to - the position in that buffer, which corresponds to the position in - the current file-visiting buffer (if any). - -‘C-c M-g G’ (‘magit-display-repository-buffer’) - This command reads and displays a Magit buffer belonging to the - current repository, without refreshing it. - -‘C-c M-g c’ (‘magit-commit’) - This transient prefix command binds the following suffix commands - along with the appropriate infix arguments and displays them in a - temporary buffer until a suffix is invoked. See *note Initiating a - Commit::. - -‘C-c M-g e’ (‘magit-edit-line-commit’) - This command makes the commit editable that added the current line. - - With a prefix argument it makes the commit editable that removes - the line, if any. The commit is determined using ‘git blame’ and - made editable using ‘git rebase --interactive’ if it is reachable - from ‘HEAD’, or by checking out the commit (or a branch that points - at it) otherwise. - - -File: magit.info, Node: Minor Mode for Buffers Visiting Blobs, Prev: Commands for Buffers Visiting Files, Up: Miscellaneous - -8.11 Minor Mode for Buffers Visiting Blobs -========================================== - -The ‘magit-blob-mode’ enables certain Magit features in blob-visiting -buffers. Such buffers can be created using ‘magit-find-file’ and some -of the commands mentioned below, which also take care of turning on this -minor mode. Currently this mode only establishes a few key bindings, -but this might be extended. - -‘p’ (‘magit-blob-previous’) - Visit the previous blob which modified the current file. - -‘n’ (‘magit-blob-next’) - Visit the next blob which modified the current file. - -‘q’ (‘magit-kill-this-buffer’) - Kill the current buffer. - - -File: magit.info, Node: Customizing, Next: Plumbing, Prev: Miscellaneous, Up: Top - -9 Customizing -************* - -Both Git and Emacs are highly customizable. Magit is both a Git -porcelain as well as an Emacs package, so it makes sense to customize it -using both Git variables as well as Emacs options. However this -flexibility doesn’t come without problems, including but not limited to -the following. - - • Some Git variables automatically have an effect in Magit without - requiring any explicit support. Sometimes that is desirable - in - other cases, it breaks Magit. - - When a certain Git setting breaks Magit but you want to keep using - that setting on the command line, then that can be accomplished by - overriding the value for Magit only by appending something like - ‘("-c" "some.variable=compatible-value")’ to - ‘magit-git-global-arguments’. - - • Certain settings like ‘fetch.prune=true’ are respected by Magit - commands (because they simply call the respective Git command) but - their value is not reflected in the respective transient buffers. - In this case the ‘--prune’ argument in ‘magit-fetch’ might be - active or inactive, but that doesn’t keep the Git variable from - being honored by the suffix commands anyway. So pruning might - happen despite the ‘--prune’ arguments being displayed in a way - that seems to indicate that no pruning will happen. - - I intend to address these and similar issues in a future release. - -* Menu: - -* Per-Repository Configuration:: -* Essential Settings:: - - -File: magit.info, Node: Per-Repository Configuration, Next: Essential Settings, Up: Customizing - -9.1 Per-Repository Configuration -================================ - -Magit can be configured on a per-repository level using both Git -variables as well as Emacs options. - - To set a Git variable for one repository only, simply set it in -‘/path/to/repo/.git/config’ instead of ‘$HOME/.gitconfig’ or -‘/etc/gitconfig’. See *note (gitman)git-config::. - - Similarly, Emacs options can be set for one repository only by -editing ‘/path/to/repo/.dir-locals.el’. See *note (emacs)Directory -Variables::. For example to disable automatic refreshes of -file-visiting buffers in just one huge repository use this: - - • ‘/path/to/huge/repo/.dir-locals.el’ - - ((nil . ((magit-refresh-buffers . nil)))) - - It might only be costly to insert certain information into Magit -buffers for repositories that are exceptionally large, in which case you -can disable the respective section inserters just for that repository: - - • ‘/path/to/tag/invested/repo/.dir-locals.el’ - - ((magit-status-mode - . ((eval . (magit-disable-section-inserter 'magit-insert-tags-header))))) - - -- Function: magit-disable-section-inserter fn - This function disables the section inserter FN in the current - repository. It is only intended for use in ‘.dir-locals.el’ and - ‘.dir-locals-2.el’. - - If you want to apply the same settings to several, but not all, -repositories then keeping the repository-local config files in sync -would quickly become annoying. To avoid that you can create config -files for certain classes of repositories (e.g., "huge repositories") -and then include those files in the per-repository config files. For -example: - - • ‘/path/to/huge/repo/.git/config’ - - [include] - path = /path/to/huge-gitconfig - - • ‘/path/to/huge-gitconfig’ - - [status] - showUntrackedFiles = no - - • ‘$HOME/.emacs.d/init.el’ - - (dir-locals-set-class-variables 'huge-git-repository - '((nil . ((magit-refresh-buffers . nil))))) - - (dir-locals-set-directory-class - "/path/to/huge/repo/" 'huge-git-repository) - - -File: magit.info, Node: Essential Settings, Prev: Per-Repository Configuration, Up: Customizing - -9.2 Essential Settings -====================== - -The next three sections list and discuss several variables that many -users might want to customize, for safety and/or performance reasons. - -* Menu: - -* Safety:: -* Performance:: -* Global Bindings:: - - -File: magit.info, Node: Safety, Next: Performance, Up: Essential Settings - -9.2.1 Safety ------------- - -This section discusses various variables that you might want to change -(or *not* change) for safety reasons. - - Git keeps *committed* changes around long enough for users to recover -changes they have accidentally been deleted. It does not do the same -for *uncommitted* changes in the working tree and not even the index -(the staging area). Because Magit makes it so easy to modify -uncommitted changes, it also makes it easy to shoot yourself in the foot -in the process. For that reason Magit provides three global modes that -save *tracked* files to work-in-progress references after or before -certain actions. See *note Wip Modes::. - - These modes are not enabled by default because of performance -concerns. Instead a lot of potentially destructive commands require -confirmation every time they are used. In many cases this can be -disabled by adding a symbol to ‘magit-no-confirm’ (see *note Completion -and Confirmation::). If you enable the various wip modes then you -should add ‘safe-with-wip’ to this list. - - Similarly it isn’t necessary to require confirmation before moving a -file to the system trash - if you trashed a file by mistake then you can -recover it from there. Option ‘magit-delete-by-moving-to-trash’ -controls whether the system trash is used, which is the case by default. -Nevertheless, ‘trash’ isn’t a member of ‘magit-no-confirm’ - you might -want to change that. - - By default buffers visiting files are automatically reverted when the -visited file changes on disk. This isn’t as risky as it might seem, but -to make an informed decision you should see *note Risk of Reverting -Automatically::. - - -File: magit.info, Node: Performance, Next: Global Bindings, Prev: Safety, Up: Essential Settings - -9.2.2 Performance ------------------ - -After Magit has run ‘git’ for side-effects, it also refreshes the -current Magit buffer and the respective status buffer. This is -necessary because otherwise outdated information might be displayed -without the user noticing. Magit buffers are updated by recreating -their content from scratch, which makes updating simpler and less -error-prone, but also more costly. Keeping it simple and just -re-creating everything from scratch is an old design decision and -departing from that will require major refactoring. - - Meanwhile you can tell Magit to only automatically refresh the -current Magit buffer, but not the status buffer. If you do that, then -the status buffer is only refreshed automatically if it is the current -buffer. - - (setq magit-refresh-status-buffer nil) - - You should also check whether any third-party packages have added -anything to ‘magit-refresh-buffer-hook’, ‘magit-pre-refresh-hook’, and -‘magit-post-refresh-hook’. If so, then check whether those additions -impact performance significantly. - - Magit can be told to refresh buffers verbosely using ‘M-x -magit-toggle-verbose-refresh’. Enabling this helps figuring out which -sections are bottlenecks. Each line printed to the ‘*Messages*’ buffer -contains a section name, the number of seconds it took to show this -section, and from 0 to 2 exclamation marks: the more exclamation marks -the slower the section is. - - Magit also reverts buffers for visited files located inside the -current repository when the visited file changes on disk. That is -implemented on top of ‘auto-revert-mode’ from the built-in library -‘autorevert’. To figure out whether that impacts performance, check -whether performance is significantly worse, when many buffers exist -and/or when some buffers visit files using TRAMP. If so, then this -should help. - - (setq auto-revert-buffer-list-filter - 'magit-auto-revert-repository-buffer-p) - - For alternative approaches see *note Automatic Reverting of -File-Visiting Buffers::. - - If you have enabled any features that are disabled by default, then -you should check whether they impact performance significantly. It’s -likely that they were not enabled by default because it is known that -they reduce performance at least in large repositories. - - If performance is only slow inside certain unusually large -repositories, then you might want to disable certain features on a -per-repository or per-repository-class basis only. See *note -Per-Repository Configuration::. For example it takes a long time to -determine the next and current tag in repository with exceptional -numbers of tags. It would therefore be a good idea to disable -‘magit-insert-tags-headers’, as explained at the mentioned node. - -* Menu: - -* Microsoft Windows Performance:: -* MacOS Performance:: - -Log Performance -............... - -When showing logs, Magit limits the number of commits initially shown in -the hope that this avoids unnecessary work. When ‘--graph’ is used, -then this unfortunately does not have the desired effect for large -histories. Junio, Git’s maintainer, said on the git mailing list -(<https://www.spinics.net/lists/git/msg232230.html>): "‘--graph’ wants -to compute the whole history and the max-count only affects the output -phase after ‘--graph’ does its computation". - - In other words, it’s not that Git is slow at outputting the -differences, or that Magit is slow at parsing the output - the problem -is that Git first goes outside and has a smoke. - - We actually work around this issue by limiting the number of commits -not only by using ‘-<N>’ but by also using a range. But unfortunately -that’s not always possible. - - When more than a few thousand commits are shown, then the use of -‘--graph’ can slow things down. - - Using ‘--color --graph’ is even slower. Magit uses code that is part -of Emacs to turn control characters into faces. That code is pretty -slow and this is quite noticeable when showing a log with many branches -and merges. For that reason ‘--color’ is not enabled by default -anymore. Consider leaving it at that. - -Diff Performance -................ - -If diffs are slow, then consider turning off some optional diff features -by setting all or some of the following variables to ‘nil’: -‘magit-diff-highlight-indentation’, ‘magit-diff-highlight-trailing’, -‘magit-diff-paint-whitespace’, ‘magit-diff-highlight-hunk-body’, and -‘magit-diff-refine-hunk’. - - When showing a commit instead of some arbitrary diff, then some -additional information is displayed. Calculating this information can -be quite expensive given certain circumstances. If looking at a commit -using ‘magit-revision-mode’ takes considerably more time than looking at -the same commit in ‘magit-diff-mode’, then consider setting -‘magit-revision-insert-related-refs’ to ‘nil’. - - When you are often confronted with diffs that contain deleted files, -then you might want to enable the ‘--irreversible-delete’ argument. If -you do that then diffs still show that a file was deleted but without -also showing the complete deleted content of the file. This argument is -not available by default, see *note (transient)Enabling and Disabling -Suffixes::. Once you have done that you should enable it and save that -setting, see *note (transient)Saving Values::. You should do this in -both the diff (‘d’) and the diff refresh (‘D’) transient popups. - -Refs Buffer Performance -....................... - -When refreshing the "references buffer" is slow, then that’s usually -because several hundred refs are being displayed. The best way to -address that is to display fewer refs, obviously. - - If you are not, or only mildly, interested in seeing the list of -tags, then start by not displaying them: - - (remove-hook 'magit-refs-sections-hook 'magit-insert-tags) - - Then you should also make sure that the listed remote branches -actually all exist. You can do so by pruning branches which no longer -exist using ‘f-pa’. - -Committing Performance -...................... - -When you initiate a commit, then Magit by default automatically shows a -diff of the changes you are about to commit. For large commits this can -take a long time, which is especially distracting when you are -committing large amounts of generated data which you don’t actually -intend to inspect before committing. This behavior can be turned off -using: - - (remove-hook 'server-switch-hook 'magit-commit-diff) - (remove-hook 'with-editor-filter-visit-hook 'magit-commit-diff) - - Then you can type ‘C-c C-d’ to show the diff when you actually want -to see it, but only then. Alternatively you can leave the hook alone -and just type ‘C-g’ in those cases when it takes too long to generate -the diff. If you do that, then you will end up with a broken diff -buffer, but doing it this way has the advantage that you usually get to -see the diff, which is useful because it increases the odds that you -spot potential issues. - - -File: magit.info, Node: Microsoft Windows Performance, Next: MacOS Performance, Up: Performance - -Microsoft Windows Performance -............................. - -In order to update the status buffer, ‘git’ has to be run a few dozen -times. That is problematic on Microsoft Windows, because that operating -system is exceptionally slow at starting processes. Sadly this is an -issue that can only be fixed by Microsoft itself, and they don’t appear -to be particularly interested in doing so. - - Beside the subprocess issue, there are also other Windows-specific -performance issues. Some of these have workarounds. The maintainers of -"Git for Windows" try to improve performance on Windows. Always use the -latest release in order to benefit from the latest performance tweaks. -Magit too tries to work around some Windows-specific issues. - - According to some sources, setting the following Git variables can -also help. - - git config --global core.preloadindex true # default since v2.1 - git config --global core.fscache true # default since v2.8 - git config --global gc.auto 256 - - You should also check whether an anti-virus program is affecting -performance. - - -File: magit.info, Node: MacOS Performance, Prev: Microsoft Windows Performance, Up: Performance - -MacOS Performance -................. - -Before Emacs 26.1 child processes were created using ‘fork’ on macOS. -That needlessly copied GUI resources, which is expensive. The result -was that forking took about 30 times as long on Darwin than on Linux, -and because Magit starts many ‘git’ processes that made quite a -difference. - - So make sure that you are using at least Emacs 26.1, in which case -the faster ‘vfork’ will be used. (The creation of child processes still -takes about twice as long on Darwin compared to Linux.) See (1) for -more information. - - Additionally, ‘git’ installed from a package manager like ‘brew’ or -‘nix’ seems to be slower than the native executable. Profile the ‘git’ -executable you’re running against the one at ‘/usr/bin/git’, and if you -notice a notable difference try using the latter as -‘magit-git-executable’. - - ---------- Footnotes ---------- - - (1) -<https://lists.gnu.org/archive/html/bug-gnu-emacs/2017-04/msg00201.html> - - -File: magit.info, Node: Global Bindings, Prev: Performance, Up: Essential Settings - -9.2.3 Global Bindings ---------------------- - - -- User Option: magit-define-global-key-bindings - This option controls which set of Magit key bindings, if any, may - be added to the global keymap, even before Magit is first used in - the current Emacs session. - - • If the value is ‘nil’, no bindings are added. - - • If ‘default’, maybe add: - - ‘C-x g’ ‘magit-status’ - ‘C-x M-g’ ‘magit-dispatch’ - ‘C-c M-g’ ‘magit-file-dispatch’ - - • If ‘recommended’, maybe add: - - ‘C-x g’ ‘magit-status’ - ‘C-c g’ ‘magit-dispatch’ - ‘C-c f’ ‘magit-file-dispatch’ - - These bindings are strongly recommended, but we cannot use - them by default, because the ‘C-c <LETTER>’ namespace is - strictly reserved for bindings added by the user (see *note - (elisp)Key Binding Conventions::). - - The bindings in the chosen set may be added when ‘after-init-hook’ - is run. Each binding is added if, and only if, at that time no - other key is bound to the same command, and no other command is - bound to the same key. In other words we try to avoid adding - bindings that are unnecessary, as well as bindings that conflict - with other bindings. - - Adding these bindings is delayed until ‘after-init-hook’ is run to - allow users to set the variable anywhere in their init file - (without having to make sure to do so before ‘magit’ is loaded or - autoloaded) and to increase the likelihood that all the potentially - conflicting user bindings have already been added. - - To set this variable use either ‘setq’ or the Custom interface. Do - not use the function ‘customize-set-variable’ because doing that - would cause Magit to be loaded immediately, when that form is - evaluated (this differs from ‘custom-set-variables’, which doesn’t - load the libraries that define the customized variables). - - Setting this variable has no effect if ‘after-init-hook’ has - already been run. - - -File: magit.info, Node: Plumbing, Next: FAQ, Prev: Customizing, Up: Top - -10 Plumbing -*********** - -The following sections describe how to use several of Magit’s core -abstractions to extend Magit itself or implement a separate extension. - - A few of the low-level features used by Magit have been factored out -into separate libraries/packages, so that they can be used by other -packages, without having to depend on Magit. See *note -(with-editor)Top:: for information about ‘with-editor’. ‘transient’ -doesn’t have a manual yet. - - If you are trying to find an unused key that you can bind to a -command provided by your own Magit extension, then checkout -<https://github.com/magit/magit/wiki/Plugin-Dispatch-Key-Registry>. - -* Menu: - -* Calling Git:: -* Section Plumbing:: -* Refreshing Buffers:: -* Conventions:: - - -File: magit.info, Node: Calling Git, Next: Section Plumbing, Up: Plumbing - -10.1 Calling Git -================ - -Magit provides many specialized functions for calling Git. All of these -functions are defined in either ‘magit-git.el’ or ‘magit-process.el’ and -have one of the prefixes ‘magit-run-’, ‘magit-call-’, ‘magit-start-’, or -‘magit-git-’ (which is also used for other things). - - All of these functions accept an indefinite number of arguments, -which are strings that specify command line arguments for Git (or in -some cases an arbitrary executable). These arguments are flattened -before being passed on to the executable; so instead of strings they can -also be lists of strings and arguments that are ‘nil’ are silently -dropped. Some of these functions also require a single mandatory -argument before these command line arguments. - - Roughly speaking, these functions run Git either to get some value or -for side-effects. The functions that return a value are useful to -collect the information necessary to populate a Magit buffer, while the -others are used to implement Magit commands. - - The functions in the value-only group always run synchronously, and -they never trigger a refresh. The function in the side-effect group can -be further divided into subgroups depending on whether they run Git -synchronously or asynchronously, and depending on whether they trigger a -refresh when the executable has finished. - -* Menu: - -* Getting a Value from Git:: -* Calling Git for Effect:: - - -File: magit.info, Node: Getting a Value from Git, Next: Calling Git for Effect, Up: Calling Git - -10.1.1 Getting a Value from Git -------------------------------- - -These functions run Git in order to get a value, an exit status, or -output. Of course you could also use them to run Git commands that have -side-effects, but that should be avoided. - - -- Function: magit-git-exit-code &rest args - Executes git with ARGS and returns its exit code. - - -- Function: magit-git-success &rest args - Executes git with ARGS and returns ‘t’ if the exit code is ‘0’, - ‘nil’ otherwise. - - -- Function: magit-git-failure &rest args - Executes git with ARGS and returns ‘t’ if the exit code is ‘1’, - ‘nil’ otherwise. - - -- Function: magit-git-true &rest args - Executes git with ARGS and returns ‘t’ if the first line printed by - git is the string "true", ‘nil’ otherwise. - - -- Function: magit-git-false &rest args - Executes git with ARGS and returns ‘t’ if the first line printed by - git is the string "false", ‘nil’ otherwise. - - -- Function: magit-git-insert &rest args - Executes git with ARGS and inserts its output at point. - - -- Function: magit-git-string &rest args - Executes git with ARGS and returns the first line of its output. - If there is no output or if it begins with a newline character, - then this returns ‘nil’. - - -- Function: magit-git-lines &rest args - Executes git with ARGS and returns its output as a list of lines. - Empty lines anywhere in the output are omitted. - - -- Function: magit-git-items &rest args - Executes git with ARGS and returns its null-separated output as a - list. Empty items anywhere in the output are omitted. - - If the value of option ‘magit-git-debug’ is non-nil and git exits - with a non-zero exit status, then warn about that in the echo area - and add a section containing git’s standard error in the current - repository’s process buffer. - - -- Function: magit-process-git destination &rest args - Calls Git synchronously in a separate process, returning its exit - code. DESTINATION specifies how to handle the output, like for - ‘call-process’, except that file handlers are supported. Enables - Cygwin’s "noglob" option during the call and ensures unix eol - conversion. - - -- Function: magit-process-file process &optional infile buffer display - &rest args - Processes files synchronously in a separate process. Identical to - ‘process-file’ but temporarily enables Cygwin’s "noglob" option - during the call and ensures unix eol conversion. - - If an error occurs when using one of the above functions, then that -is usually due to a bug, i.e., using an argument which is not actually -supported. Such errors are usually not reported, but when they occur we -need to be able to debug them. - - -- User Option: magit-git-debug - Whether to report errors that occur when using ‘magit-git-insert’, - ‘magit-git-string’, ‘magit-git-lines’, or ‘magit-git-items’. This - does not actually raise an error. Instead a message is shown in - the echo area, and git’s standard error is insert into a new - section in the current repository’s process buffer. - - -- Function: magit-git-str &rest args - This is a variant of ‘magit-git-string’ that ignores the option - ‘magit-git-debug’. It is mainly intended to be used while handling - errors in functions that do respect that option. Using such a - function while handing an error could cause yet another error and - therefore lead to an infinite recursion. You probably won’t ever - need to use this function. - - -File: magit.info, Node: Calling Git for Effect, Prev: Getting a Value from Git, Up: Calling Git - -10.1.2 Calling Git for Effect ------------------------------ - -These functions are used to run git to produce some effect. Most Magit -commands that actually run git do so by using such a function. - - Because we do not need to consume git’s output when using these -functions, their output is instead logged into a per-repository buffer, -which can be shown using ‘$’ from a Magit buffer or ‘M-x magit-process’ -elsewhere. - - These functions can have an effect in two distinct ways. Firstly, -running git may change something, i.e., create or push a new commit. -Secondly, that change may require that Magit buffers are refreshed to -reflect the changed state of the repository. But refreshing isn’t -always desirable, so only some of these functions do perform such a -refresh after git has returned. - - Sometimes it is useful to run git asynchronously. For example, when -the user has just initiated a push, then there is no reason to make her -wait until that has completed. In other cases it makes sense to wait -for git to complete before letting the user do something else. For -example after staging a change it is useful to wait until after the -refresh because that also automatically moves to the next change. - - -- Function: magit-call-git &rest args - Calls git synchronously with ARGS. - - -- Function: magit-call-process program &rest args - Calls PROGRAM synchronously with ARGS. - - -- Function: magit-run-git &rest args - Calls git synchronously with ARGS and then refreshes. - - -- Function: magit-run-git-with-input &rest args - Calls git synchronously with ARGS and sends it the content of the - current buffer on standard input. - - If the current buffer’s ‘default-directory’ is on a remote - filesystem, this function actually runs git asynchronously. But - then it waits for the process to return, so the function itself is - synchronous. - - -- Function: magit-git &rest args - Calls git synchronously with ARGS for side-effects only. This - function does not refresh the buffer. - - -- Function: magit-git-wash washer &rest args - Execute Git with ARGS, inserting washed output at point. Actually - first insert the raw output at point. If there is no output call - ‘magit-cancel-section’. Otherwise temporarily narrow the buffer to - the inserted text, move to its beginning, and then call function - WASHER with ARGS as its sole argument. - - And now for the asynchronous variants. - - -- Function: magit-run-git-async &rest args - Start Git, prepare for refresh, and return the process object. - ARGS is flattened and then used as arguments to Git. - - Display the command line arguments in the echo area. - - After Git returns some buffers are refreshed: the buffer that was - current when this function was called (if it is a Magit buffer and - still alive), as well as the respective Magit status buffer. - Unmodified buffers visiting files that are tracked in the current - repository are reverted if ‘magit-revert-buffers’ is non-nil. - - -- Function: magit-run-git-with-editor &rest args - Export GIT_EDITOR and start Git. Also prepare for refresh and - return the process object. ARGS is flattened and then used as - arguments to Git. - - Display the command line arguments in the echo area. - - After Git returns some buffers are refreshed: the buffer that was - current when this function was called (if it is a Magit buffer and - still alive), as well as the respective Magit status buffer. - - -- Function: magit-start-git input &rest args - Start Git, prepare for refresh, and return the process object. - - If INPUT is non-nil, it has to be a buffer or the name of an - existing buffer. The buffer content becomes the processes standard - input. - - Option ‘magit-git-executable’ specifies the Git executable and - option ‘magit-git-global-arguments’ specifies constant arguments. - The remaining arguments ARGS specify arguments to Git. They are - flattened before use. - - After Git returns, some buffers are refreshed: the buffer that was - current when this function was called (if it is a Magit buffer and - still alive), as well as the respective Magit status buffer. - Unmodified buffers visiting files that are tracked in the current - repository are reverted if ‘magit-revert-buffers’ is non-nil. - - -- Function: magit-start-process &rest args - Start PROGRAM, prepare for refresh, and return the process object. - - If optional argument INPUT is non-nil, it has to be a buffer or the - name of an existing buffer. The buffer content becomes the - processes standard input. - - The process is started using ‘start-file-process’ and then setup to - use the sentinel ‘magit-process-sentinel’ and the filter - ‘magit-process-filter’. Information required by these functions is - stored in the process object. When this function returns the - process has not started to run yet so it is possible to override - the sentinel and filter. - - After the process returns, ‘magit-process-sentinel’ refreshes the - buffer that was current when ‘magit-start-process’ was called (if - it is a Magit buffer and still alive), as well as the respective - Magit status buffer. Unmodified buffers visiting files that are - tracked in the current repository are reverted if - ‘magit-revert-buffers’ is non-nil. - - -- Variable: magit-this-process - The child process which is about to start. This can be used to - change the filter and sentinel. - - -- Variable: magit-process-raise-error - When this is non-nil, then ‘magit-process-sentinel’ raises an error - if git exits with a non-zero exit status. For debugging purposes. - - -File: magit.info, Node: Section Plumbing, Next: Refreshing Buffers, Prev: Calling Git, Up: Plumbing - -10.2 Section Plumbing -===================== - -* Menu: - -* Creating Sections:: -* Section Selection:: -* Matching Sections:: - - -File: magit.info, Node: Creating Sections, Next: Section Selection, Up: Section Plumbing - -10.2.1 Creating Sections ------------------------- - - -- Macro: magit-insert-section &rest args - Insert a section at point. - - TYPE is the section type, a symbol. Many commands that act on the - current section behave differently depending on that type. Also if - a variable ‘magit-TYPE-section-map’ exists, then use that as the - text-property ‘keymap’ of all text belonging to the section (but - this may be overwritten in subsections). TYPE can also have the - form ‘(eval FORM)’ in which case FORM is evaluated at runtime. - - Optional VALUE is the value of the section, usually a string that - is required when acting on the section. - - When optional HIDE is non-nil collapse the section body by default, - i.e., when first creating the section, but not when refreshing the - buffer. Otherwise, expand it by default. This can be overwritten - using ‘magit-section-set-visibility-hook’. When a section is - recreated during a refresh, then the visibility of predecessor is - inherited and HIDE is ignored (but the hook is still honored). - - BODY is any number of forms that actually insert the section’s - heading and body. Optional NAME, if specified, has to be a symbol, - which is then bound to the struct of the section being inserted. - - Before BODY is evaluated the ‘start’ of the section object is set - to the value of ‘point’ and after BODY was evaluated its ‘end’ is - set to the new value of ‘point’; BODY is responsible for moving - ‘point’ forward. - - If it turns out inside BODY that the section is empty, then - ‘magit-cancel-section’ can be used to abort and remove all traces - of the partially inserted section. This can happen when creating a - section by washing Git’s output and Git didn’t actually output - anything this time around. - - -- Function: magit-insert-heading &rest args - Insert the heading for the section currently being inserted. - - This function should only be used inside ‘magit-insert-section’. - - When called without any arguments, then just set the ‘content’ slot - of the object representing the section being inserted to a marker - at ‘point’. The section should only contain a single line when - this function is used like this. - - When called with arguments ARGS, which have to be strings, then - insert those strings at point. The section should not contain any - text before this happens and afterwards it should again only - contain a single line. If the ‘face’ property is set anywhere - inside any of these strings, then insert all of them unchanged. - Otherwise use the ‘magit-section-heading’ face for all inserted - text. - - The ‘content’ property of the section struct is the end of the - heading (which lasts from ‘start’ to ‘content’) and the beginning - of the body (which lasts from ‘content’ to ‘end’). If the value of - ‘content’ is nil, then the section has no heading and its body - cannot be collapsed. If a section does have a heading then its - height must be exactly one line, including a trailing newline - character. This isn’t enforced; you are responsible for getting it - right. The only exception is that this function does insert a - newline character if necessary. - - -- Function: magit-cancel-section - Cancel the section currently being inserted. This exits the - innermost call to ‘magit-insert-section’ and removes all traces of - what has already happened inside that call. - - -- Function: magit-define-section-jumper sym title &optional value - Define an interactive function to go to section SYM. TITLE is the - displayed title of the section. - - -File: magit.info, Node: Section Selection, Next: Matching Sections, Prev: Creating Sections, Up: Section Plumbing - -10.2.2 Section Selection ------------------------- - - -- Function: magit-current-section - Return the section at point. - - -- Function: magit-region-sections &optional condition multiple - Return a list of the selected sections. - - When the region is active and constitutes a valid section - selection, then return a list of all selected sections. This is - the case when the region begins in the heading of a section and - ends in the heading of the same section or in that of a sibling - section. If optional MULTIPLE is non-nil, then the region cannot - begin and end in the same section. - - When the selection is not valid, then return nil. In this case, - most commands that can act on the selected sections will instead - act on the section at point. - - When the region looks like it would in any other buffer then the - selection is invalid. When the selection is valid then the region - uses the ‘magit-section-highlight’ face. This does not apply to - diffs where things get a bit more complicated, but even here if the - region looks like it usually does, then that’s not a valid - selection as far as this function is concerned. - - If optional CONDITION is non-nil, then the selection not only has - to be valid; all selected sections additionally have to match - CONDITION, or nil is returned. See ‘magit-section-match’ for the - forms CONDITION can take. - - -- Function: magit-region-values &optional condition multiple - Return a list of the values of the selected sections. - - Return the values that themselves would be returned by - ‘magit-region-sections’ (which see). - - -File: magit.info, Node: Matching Sections, Prev: Section Selection, Up: Section Plumbing - -10.2.3 Matching Sections ------------------------- - -‘M-x magit-describe-section-briefly’ - Show information about the section at point. This command is - intended for debugging purposes. - - -- Function: magit-section-ident section - Return an unique identifier for SECTION. The return value has the - form ‘((TYPE . VALUE)...)’. - - -- Function: magit-get-section ident &optional root - Return the section identified by IDENT. IDENT has to be a list as - returned by ‘magit-section-ident’. - - -- Function: magit-section-match condition &optional section - Return ‘t’ if SECTION matches CONDITION. SECTION defaults to the - section at point. If SECTION is not specified and there also is no - section at point, then return ‘nil’. - - CONDITION can take the following forms: - • ‘(CONDITION...)’ - - matches if any of the CONDITIONs matches. - - • ‘[CLASS...]’ - - matches if the section’s class is the same as the first CLASS - or a subclass of that; the section’s parent class matches the - second CLASS; and so on. - - • ‘[* CLASS...]’ - - matches sections that match ‘[CLASS...]’ and also recursively - all their child sections. - - • ‘CLASS’ - - matches if the section’s class is the same as CLASS or a - subclass of that; regardless of the classes of the parent - sections. - - Each CLASS should be a class symbol, identifying a class that - derives from ‘magit-section’. For backward compatibility CLASS can - also be a "type symbol". A section matches such a symbol if the - value of its ‘type’ slot is ‘eq’. If a type symbol has an entry in - ‘magit--section-type-alist’, then a section also matches that type - if its class is a subclass of the class that corresponds to the - type as per that alist. - - Note that it is not necessary to specify the complete section - lineage as printed by ‘magit-describe-section-briefly’, unless of - course you want to be that precise. - - -- Function: magit-section-value-if condition &optional section - If the section at point matches CONDITION, then return its value. - - If optional SECTION is non-nil then test whether that matches - instead. If there is no section at point and SECTION is nil, then - return nil. If the section does not match, then return nil. - - See ‘magit-section-match’ for the forms CONDITION can take. - - -- Function: magit-section-case &rest clauses - Choose among clauses on the type of the section at point. - - Each clause looks like (CONDITION BODY...). The type of the - section is compared against each CONDITION; the BODY forms of the - first match are evaluated sequentially and the value of the last - form is returned. Inside BODY the symbol ‘it’ is bound to the - section at point. If no clause succeeds or if there is no section - at point return nil. - - See ‘magit-section-match’ for the forms CONDITION can take. - Additionally a CONDITION of t is allowed in the final clause and - matches if no other CONDITION match, even if there is no section at - point. - - -- Variable: magit-root-section - The root section in the current buffer. All other sections are - descendants of this section. The value of this variable is set by - ‘magit-insert-section’ and you should never modify it. - - For diff related sections a few additional tools exist. - - -- Function: magit-diff-type &optional section - Return the diff type of SECTION. - - The returned type is one of the symbols ‘staged’, ‘unstaged’, - ‘committed’, or ‘undefined’. This type serves a similar purpose as - the general type common to all sections (which is stored in the - ‘type’ slot of the corresponding ‘magit-section’ struct) but takes - additional information into account. When the SECTION isn’t - related to diffs and the buffer containing it also isn’t a - diff-only buffer, then return nil. - - Currently the type can also be one of ‘tracked’ and ‘untracked’, - but these values are not handled explicitly in every place they - should be. A possible fix could be to just return nil here. - - The section has to be a ‘diff’ or ‘hunk’ section, or a section - whose children are of type ‘diff’. If optional SECTION is nil, - return the diff type for the current section. In buffers whose - major mode is ‘magit-diff-mode’ SECTION is ignored and the type is - determined using other means. In ‘magit-revision-mode’ buffers the - type is always ‘committed’. - - -- Function: magit-diff-scope &optional section strict - Return the diff scope of SECTION or the selected section(s). - - A diff’s "scope" describes what part of a diff is selected, it is a - symbol, one of ‘region’, ‘hunk’, ‘hunks’, ‘file’, ‘files’, or - ‘list’. Do not confuse this with the diff "type", as returned by - ‘magit-diff-type’. - - If optional SECTION is non-nil, then return the scope of that, - ignoring the sections selected by the region. Otherwise return the - scope of the current section, or if the region is active and - selects a valid group of diff related sections, the type of these - sections, i.e., ‘hunks’ or ‘files’. If SECTION (or if the current - section that is nil) is a ‘hunk’ section and the region starts and - ends inside the body of a that section, then the type is ‘region’. - - If optional STRICT is non-nil then return nil if the diff type of - the section at point is ‘untracked’ or the section at point is not - actually a ‘diff’ but a ‘diffstat’ section. - - -File: magit.info, Node: Refreshing Buffers, Next: Conventions, Prev: Section Plumbing, Up: Plumbing - -10.3 Refreshing Buffers -======================= - -All commands that create a new Magit buffer or change what is being -displayed in an existing buffer do so by calling ‘magit-mode-setup’. -Among other things, that function sets the buffer local values of -‘default-directory’ (to the top-level of the repository), -‘magit-refresh-function’, and ‘magit-refresh-args’. - - Buffers are refreshed by calling the function that is the local value -of ‘magit-refresh-function’ (a function named ‘magit-*-refresh-buffer’, -where ‘*’ may be something like ‘diff’) with the value of -‘magit-refresh-args’ as arguments. - - -- Macro: magit-mode-setup buffer switch-func mode refresh-func - &optional refresh-args - This function displays and selects BUFFER, turns on MODE, and - refreshes a first time. - - This function displays and optionally selects BUFFER by calling - ‘magit-mode-display-buffer’ with BUFFER, MODE and SWITCH-FUNC as - arguments. Then it sets the local value of - ‘magit-refresh-function’ to REFRESH-FUNC and that of - ‘magit-refresh-args’ to REFRESH-ARGS. Finally it creates the - buffer content by calling REFRESH-FUNC with REFRESH-ARGS as - arguments. - - All arguments are evaluated before switching to BUFFER. - - -- Function: magit-mode-display-buffer buffer mode &optional - switch-function - This function display BUFFER in some window and select it. BUFFER - may be a buffer or a string, the name of a buffer. The buffer is - returned. - - Unless BUFFER is already displayed in the selected frame, store the - previous window configuration as a buffer local value, so that it - can later be restored by ‘magit-mode-bury-buffer’. - - The buffer is displayed and selected using SWITCH-FUNCTION. If - that is ‘nil’ then ‘pop-to-buffer’ is used if the current buffer’s - major mode derives from ‘magit-mode’. Otherwise ‘switch-to-buffer’ - is used. - - -- Variable: magit-refresh-function - The value of this buffer-local variable is the function used to - refresh the current buffer. It is called with ‘magit-refresh-args’ - as arguments. - - -- Variable: magit-refresh-args - The list of arguments used by ‘magit-refresh-function’ to refresh - the current buffer. ‘magit-refresh-function’ is called with these - arguments. - - The value is usually set using ‘magit-mode-setup’, but in some - cases it’s also useful to provide commands that can change the - value. For example, the ‘magit-diff-refresh’ transient can be used - to change any of the arguments used to display the diff, without - having to specify again which differences should be shown, but - ‘magit-diff-more-context’, ‘magit-diff-less-context’ and - ‘magit-diff-default-context’ change just the ‘-U<N>’ argument. In - both case this is done by changing the value of this variable and - then calling this ‘magit-refresh-function’. - - -File: magit.info, Node: Conventions, Prev: Refreshing Buffers, Up: Plumbing - -10.4 Conventions -================ - -Also see *note Completion and Confirmation::. - -* Menu: - -* Theming Faces:: - - -File: magit.info, Node: Theming Faces, Up: Conventions - -10.4.1 Theming Faces --------------------- - -The default theme uses blue for local branches, green for remote -branches, and goldenrod (brownish yellow) for tags. When creating a new -theme, you should probably follow that example. If your theme already -uses other colors, then stick to that. - - In older releases these reference faces used to have a background -color and a box around them. The basic default faces no longer do so, -to make Magit buffers much less noisy, and you should follow that -example at least with regards to boxes. (Boxes were used in the past to -work around a conflict between the highlighting overlay and text -property backgrounds. That’s no longer necessary because highlighting -no longer causes other background colors to disappear.) Alternatively -you can keep the background color and/or box, but then have to take -special care to adjust ‘magit-branch-current’ accordingly. By default -it looks mostly like ‘magit-branch-local’, but with a box (by default -the former is the only face that uses a box, exactly so that it sticks -out). If the former also uses a box, then you have to make sure that it -differs in some other way from the latter. - - The most difficult faces to theme are those related to diffs, -headings, highlighting, and the region. There are faces that fall into -all four groups - expect to spend some time getting this right. - - The ‘region’ face in the default theme, in both the light and dark -variants, as well as in many other themes, distributed with Emacs or by -third-parties, is very ugly. It is common to use a background color -that really sticks out, which is ugly but if that were the only problem -then it would be acceptable. Unfortunately many themes also set the -foreground color, which ensures that all text within the region is -readable. Without doing that there might be cases where some foreground -color is too close to the region background color to still be readable. -But it also means that text within the region loses all syntax -highlighting. - - I consider the work that went into getting the ‘region’ face right to -be a good indicator for the general quality of a theme. My -recommendation for the ‘region’ face is this: use a background color -slightly different from the background color of the ‘default’ face, and -do not set the foreground color at all. So for a light theme you might -use a light (possibly tinted) gray as the background color of ‘default’ -and a somewhat darker gray for the background of ‘region’. That should -usually be enough to not collide with the foreground color of any other -face. But if some other faces also set a light gray as background -color, then you should also make sure it doesn’t collide with those (in -some cases it might be acceptable though). - - Magit only uses the ‘region’ face when the region is "invalid" by its -own definition. In a Magit buffer the region is used to either select -multiple sibling sections, so that commands which support it act on all -of these sections instead of just the current section, or to select -lines within a single hunk section. In all other cases, the section is -considered invalid and Magit won’t act on it. But such invalid sections -happen, either because the user has not moved point enough yet to make -it valid or because she wants to use a non-magit command to act on the -region, e.g., ‘kill-region’. - - So using the regular ‘region’ face for invalid sections is a feature. -It tells the user that Magit won’t be able to act on it. It’s -acceptable if that face looks a bit odd and even (but less so) if it -collides with the background colors of section headings and other things -that have a background color. - - Magit highlights the current section. If a section has subsections, -then all of them are highlighted. This is done using faces that have -"highlight" in their names. For most sections, -‘magit-section-highlight’ is used for both the body and the heading. -Like the ‘region’ face, it should only set the background color to -something similar to that of ‘default’. The highlight background color -must be different from both the ‘region’ background color and the -‘default’ background color. - - For diff related sections Magit uses various faces to highlight -different parts of the selected section(s). Note that hunk headings, -unlike all other section headings, by default have a background color, -because it is useful to have very visible separators between hunks. -That face ‘magit-diff-hunk-heading’, should be different from both -‘magit-diff-hunk-heading-highlight’ and ‘magit-section-highlight’, as -well as from ‘magit-diff-context’ and ‘magit-diff-context-highlight’. -By default we do that by changing the foreground color. Changing the -background color would lead to complications, and there are already -enough we cannot get around. (Also note that it is generally a good -idea for section headings to always be bold, but only for sections that -have subsections). - - When there is a valid region selecting diff-related sibling sections, -i.e., multiple files or hunks, then the bodies of all these sections use -the respective highlight faces, but additionally the headings instead -use one of the faces ‘magit-diff-file-heading-selection’ or -‘magit-diff-hunk-heading-selection’. These faces have to be different -from the regular highlight variants to provide explicit visual -indication that the region is active. - - When theming diff related faces, start by setting the option -‘magit-diff-refine-hunk’ to ‘all’. You might personally prefer to only -refine the current hunk or not use hunk refinement at all, but some of -the users of your theme want all hunks to be refined, so you have to -cater to that. - - (Also turn on ‘magit-diff-highlight-indentation’, -‘magit-diff-highlight-trailing’, and ‘magit-diff-paint-whitespace’; and -insert some whitespace errors into the code you use for testing.) - - For added lines you have to adjust three faces: ‘magit-diff-added’, -‘magit-diff-added-highlight’, and ‘diff-refined-added’. Make sure that -the latter works well with both of the former, as well as ‘smerge-other’ -and ‘diff-added’. Then do the same for the removed lines, context -lines, lines added by us, and lines added by them. Also make sure the -respective added, removed, and context faces use approximately the same -saturation for both the highlighted and unhighlighted variants. Also -make sure the file and diff headings work nicely with context lines -(e.g., make them look different). Line faces should set both the -foreground and the background color. For example, for added lines use -two different greens. - - It’s best if the foreground color of both the highlighted and the -unhighlighted variants are the same, so you will need to have to find a -color that works well on the highlight and unhighlighted background, the -refine background, and the highlight context background. When there is -an hunk internal region, then the added- and removed-lines background -color is used only within that region. Outside the region the -highlighted context background color is used. This makes it easier to -see what is being staged. With an hunk internal region the hunk heading -is shown using ‘magit-diff-hunk-heading-selection’, and so are the thin -lines that are added around the lines that fall within the region. The -background color of that has to be distinct enough from the various -other involved background colors. - - Nobody said this would be easy. If your theme restricts itself to a -certain set of colors, then you should make an exception here. -Otherwise it would be impossible to make the diffs look good in each and -every variation. Actually you might want to just stick to the default -definitions for these faces. You have been warned. Also please note -that if you do not get this right, this will in some cases look to users -like bugs in Magit - so please do it right or not at all. - - -File: magit.info, Node: FAQ, Next: Debugging Tools, Prev: Plumbing, Up: Top - -Appendix A FAQ -************** - -The next two nodes lists frequently asked questions. For a list of -frequently *and recently* asked questions, i.e., questions that haven’t -made it into the manual yet, see -<https://github.com/magit/magit/wiki/FAQ>. - - Please also see *note Debugging Tools::. - -* Menu: - -* FAQ - How to ...?:: -* FAQ - Issues and Errors:: - - -File: magit.info, Node: FAQ - How to ...?, Next: FAQ - Issues and Errors, Up: FAQ - -A.1 FAQ - How to ...? -===================== - -* Menu: - -* How to pronounce Magit?:: -* How to show git's output?:: -* How to install the gitman info manual?:: -* How to show diffs for gpg-encrypted files?:: -* How does branching and pushing work?:: -* Should I disable VC?:: - - -File: magit.info, Node: How to pronounce Magit?, Next: How to show git's output?, Up: FAQ - How to ...? - -A.1.1 How to pronounce Magit? ------------------------------ - -Either ‘mu[m's] git’ or ‘magi{c => t}’ is fine. - - The slogan is "It’s Magit! The magical Git client", so it makes -sense to pronounce Magit like magic, while taking into account that C -and T do not sound the same. - - The German "Magie" is not pronounced the same as the English "magic", -so if you speak German then you can use the above rationale to justify -using the former pronunciation; ‘Mag{ie => it}’. - - You can also choose to use the former pronunciation just because you -like it better. - - Also see <https://magit.vc/assets/videos/magic.mp4>. Also see -<https://emacs.stackexchange.com/questions/13696>. - - -File: magit.info, Node: How to show git's output?, Next: How to install the gitman info manual?, Prev: How to pronounce Magit?, Up: FAQ - How to ...? - -A.1.2 How to show git’s output? -------------------------------- - -To show the output of recently run git commands, press ‘$’ (or, if that -isn’t available, ‘M-x magit-process-buffer’). This will show a buffer -containing a section per git invocation; as always press ‘TAB’ to expand -or collapse them. - - By default, git’s output is only inserted into the process buffer if -it is run for side-effects. When the output is consumed in some way, -also inserting it into the process buffer would be too expensive. For -debugging purposes, it’s possible to do so anyway by setting -‘magit-git-debug’ to ‘t’. - - -File: magit.info, Node: How to install the gitman info manual?, Next: How to show diffs for gpg-encrypted files?, Prev: How to show git's output?, Up: FAQ - How to ...? - -A.1.3 How to install the gitman info manual? --------------------------------------------- - -Git’s manpages can be exported as an info manual called ‘gitman’. -Magit’s own info manual links to nodes in that manual instead of the -actual manpages because Info doesn’t support linking to manpages. - - Unfortunately some distributions do not install the ‘gitman’ manual -by default and you will have to install a separate documentation package -to get it. - - Magit patches Info adding the ability to visit links to the ‘gitman’ -Info manual by instead viewing the respective manpage. If you prefer -that approach, then set the value of ‘magit-view-git-manual-method’ to -one of the supported packages ‘man’ or ‘woman’, e.g.: - - (setq magit-view-git-manual-method 'man) - - -File: magit.info, Node: How to show diffs for gpg-encrypted files?, Next: How does branching and pushing work?, Prev: How to install the gitman info manual?, Up: FAQ - How to ...? - -A.1.4 How to show diffs for gpg-encrypted files? ------------------------------------------------- - -Git supports showing diffs for encrypted files, but has to be told to do -so. Since Magit just uses Git to get the diffs, configuring Git also -affects the diffs displayed inside Magit. - - git config --global diff.gpg.textconv "gpg --no-tty --decrypt" - echo "*.gpg filter=gpg diff=gpg" > .gitattributes - - -File: magit.info, Node: How does branching and pushing work?, Next: Should I disable VC?, Prev: How to show diffs for gpg-encrypted files?, Up: FAQ - How to ...? - -A.1.5 How does branching and pushing work? ------------------------------------------- - -Please see *note Branching:: and -<https://emacsair.me/2016/01/17/magit-2.4> - - -File: magit.info, Node: Should I disable VC?, Prev: How does branching and pushing work?, Up: FAQ - How to ...? - -A.1.6 Should I disable VC? --------------------------- - -If you don’t use VC (the built-in version control interface) then you -might be tempted to disable it, not least because we used to recommend -that you do that. - - We no longer recommend that you disable VC. Doing so would break -useful third-party packages (such as ‘diff-hl’), which depend on VC -being enabled. - - If you choose to disable VC anyway, then you can do so by changing -the value of ‘vc-handled-backends’. - - -File: magit.info, Node: FAQ - Issues and Errors, Prev: FAQ - How to ...?, Up: FAQ - -A.2 FAQ - Issues and Errors -=========================== - -* Menu: - -* Magit is slow:: -* I changed several thousand files at once and now Magit is unusable:: -* I am having problems committing:: -* I am using MS Windows and cannot push with Magit:: -* I am using macOS and SOMETHING works in shell, but not in Magit: I am using macOS and SOMETHING works in shell but not in Magit. -* Expanding a file to show the diff causes it to disappear:: -* Point is wrong in the COMMIT_EDITMSG buffer:: -* The mode-line information isn't always up-to-date:: -* A branch and tag sharing the same name breaks SOMETHING:: -* My Git hooks work on the command-line but not inside Magit:: -* git-commit-mode isn't used when committing from the command-line:: -* Point ends up inside invisible text when jumping to a file-visiting buffer:: -* I am no longer able to save popup defaults:: - - -File: magit.info, Node: Magit is slow, Next: I changed several thousand files at once and now Magit is unusable, Up: FAQ - Issues and Errors - -A.2.1 Magit is slow -------------------- - -See *note Performance:: and *note I changed several thousand files at -once and now Magit is unusable::. - - -File: magit.info, Node: I changed several thousand files at once and now Magit is unusable, Next: I am having problems committing, Prev: Magit is slow, Up: FAQ - Issues and Errors - -A.2.2 I changed several thousand files at once and now Magit is unusable ------------------------------------------------------------------------- - -Magit is currently not expected to work well under such conditions. It -sure would be nice if it did. Reaching satisfactory performance under -such conditions will require some heavy refactoring. This is no small -task but I hope to eventually find the time to make it happen. - - But for now we recommend you use the command line to complete this -one commit. Also see *note Performance::. - - -File: magit.info, Node: I am having problems committing, Next: I am using MS Windows and cannot push with Magit, Prev: I changed several thousand files at once and now Magit is unusable, Up: FAQ - Issues and Errors - -A.2.3 I am having problems committing -------------------------------------- - -That likely means that Magit is having problems finding an appropriate -emacsclient executable. See *note (with-editor)Configuring -With-Editor:: and *note (with-editor)Debugging::. - - -File: magit.info, Node: I am using MS Windows and cannot push with Magit, Next: I am using macOS and SOMETHING works in shell but not in Magit, Prev: I am having problems committing, Up: FAQ - Issues and Errors - -A.2.4 I am using MS Windows and cannot push with Magit ------------------------------------------------------- - -It’s almost certain that Magit is only incidental to this issue. It is -much more likely that this is a configuration issue, even if you can -push on the command line. - - Detailed setup instructions can be found at -<https://github.com/magit/magit/wiki/Pushing-with-Magit-from-Windows>. - - -File: magit.info, Node: I am using macOS and SOMETHING works in shell but not in Magit, Next: Expanding a file to show the diff causes it to disappear, Prev: I am using MS Windows and cannot push with Magit, Up: FAQ - Issues and Errors - -A.2.5 I am using macOS and SOMETHING works in shell, but not in Magit ---------------------------------------------------------------------- - -This usually occurs because Emacs doesn’t have the same environment -variables as your shell. Try installing and configuring -<https://github.com/purcell/exec-path-from-shell>. By default it -synchronizes ‘$PATH’, which helps Magit find the same ‘git’ as the one -you are using on the shell. - - If SOMETHING is "passphrase caching with gpg-agent for commit and/or -tag signing", then you’ll also need to synchronize ‘$GPG_AGENT_INFO’. - - -File: magit.info, Node: Expanding a file to show the diff causes it to disappear, Next: Point is wrong in the COMMIT_EDITMSG buffer, Prev: I am using macOS and SOMETHING works in shell but not in Magit, Up: FAQ - Issues and Errors - -A.2.6 Expanding a file to show the diff causes it to disappear --------------------------------------------------------------- - -This is probably caused by a customization of a ‘diff.*’ Git variable. -You probably set that variable for a reason, and should therefore only -undo that setting in Magit by customizing ‘magit-git-global-arguments’. - - -File: magit.info, Node: Point is wrong in the COMMIT_EDITMSG buffer, Next: The mode-line information isn't always up-to-date, Prev: Expanding a file to show the diff causes it to disappear, Up: FAQ - Issues and Errors - -A.2.7 Point is wrong in the ‘COMMIT_EDITMSG’ buffer ---------------------------------------------------- - -Neither Magit nor ‘git-commit‘ fiddle with point in the buffer used to -write commit messages, so something else must be doing it. - - You have probably globally enabled a mode which restores point in -file-visiting buffers. It might be a bit surprising, but when you write -a commit message, then you are actually editing a file. - - So you have to figure out which package is doing it. ‘saveplace’, -‘pointback’, and ‘session’ are likely candidates. These snippets might -help: - - (setq session-name-disable-regexp "\\(?:\\`'\\.git/[A-Z_]+\\'\\)") - - (with-eval-after-load 'pointback - (lambda () - (when (or git-commit-mode git-rebase-mode) - (pointback-mode -1)))) - - -File: magit.info, Node: The mode-line information isn't always up-to-date, Next: A branch and tag sharing the same name breaks SOMETHING, Prev: Point is wrong in the COMMIT_EDITMSG buffer, Up: FAQ - Issues and Errors - -A.2.8 The mode-line information isn’t always up-to-date -------------------------------------------------------- - -Magit is not responsible for the version control information that is -being displayed in the mode-line and looks something like ‘Git-master’. -The built-in "Version Control" package, also known as "VC", updates that -information, and can be told to do so more often: - - (setq auto-revert-check-vc-info t) - - But doing so isn’t good for performance. For more (overly -optimistic) information see *note (emacs)VC Mode Line::. - - If you don’t really care about seeing this information in the -mode-line, but just don’t want to see _incorrect_ information, then -consider simply not displaying it in the mode-line: - - (setq-default mode-line-format - (delete '(vc-mode vc-mode) mode-line-format)) - - -File: magit.info, Node: A branch and tag sharing the same name breaks SOMETHING, Next: My Git hooks work on the command-line but not inside Magit, Prev: The mode-line information isn't always up-to-date, Up: FAQ - Issues and Errors - -A.2.9 A branch and tag sharing the same name breaks SOMETHING -------------------------------------------------------------- - -Or more generally, ambiguous refnames break SOMETHING. - - Magit assumes that refs are named non-ambiguously across the -"refs/heads/", "refs/tags/", and "refs/remotes/" namespaces (i.e., all -the names remain unique when those prefixes are stripped). We consider -ambiguous refnames unsupported and recommend that you use a -non-ambiguous naming scheme. However, if you do work with a repository -that has ambiguous refnames, please report any issues you encounter, so -that we can investigate whether there is a simple fix. - - -File: magit.info, Node: My Git hooks work on the command-line but not inside Magit, Next: git-commit-mode isn't used when committing from the command-line, Prev: A branch and tag sharing the same name breaks SOMETHING, Up: FAQ - Issues and Errors - -A.2.10 My Git hooks work on the command-line but not inside Magit ------------------------------------------------------------------ - -When Magit calls ‘git’ it adds a few global arguments including -‘--literal-pathspecs’ and the ‘git’ process started by Magit then passes -that setting on to other ‘git’ process it starts itself. It does so by -setting the environment variable ‘GIT_LITERAL_PATHSPECS’, not by calling -subprocesses with the ‘--literal-pathspecs’ argument. You can therefore -override this setting in hook scripts using ‘unset -GIT_LITERAL_PATHSPECS’. - - -File: magit.info, Node: git-commit-mode isn't used when committing from the command-line, Next: Point ends up inside invisible text when jumping to a file-visiting buffer, Prev: My Git hooks work on the command-line but not inside Magit, Up: FAQ - Issues and Errors - -A.2.11 ‘git-commit-mode’ isn’t used when committing from the command-line -------------------------------------------------------------------------- - -The reason for this is that ‘git-commit.el’ has not been loaded yet -and/or that the server has not been started yet. These things have -always already been taken care of when you commit from Magit because in -order to do so, Magit has to be loaded and doing that involves loading -‘git-commit’ and starting the server. - - If you want to commit from the command-line, then you have to take -care of these things yourself. Your ‘init.el’ file should contain: - - (require 'git-commit) - (server-mode) - - Instead of ‘(require ’git-commit)‘ you may also use: - - (load "/path/to/magit-autoloads.el") - - You might want to do that because loading ‘git-commit’ causes large -parts of Magit to be loaded. - - There are also some variations of ‘(server-mode)’ that you might want -to try. Personally I use: - - (use-package server - :config (or (server-running-p) (server-mode))) - - Now you can use: - - $ emacs& - $ EDITOR=emacsclient git commit - - However you cannot use: - - $ killall emacs - $ EDITOR="emacsclient --alternate-editor emacs" git commit - - This will actually end up using ‘emacs’, not ‘emacsclient’. If you -do this, then you can still edit the commit message but -‘git-commit-mode’ won’t be used and you have to exit ‘emacs’ to finish -the process. - - Tautology ahead. If you want to be able to use ‘emacsclient’ to -connect to a running ‘emacs’ instance, even though no ‘emacs’ instance -is running, then you cannot use ‘emacsclient’ directly. - - Instead you have to create a script that does something like this: - - Try to use ‘emacsclient’ (without using ‘--alternate-editor’). If -that succeeds, do nothing else. Otherwise start ‘emacs &’ (and -‘init.el’ must call ‘server-start’) and try to use ‘emacsclient’ again. - - -File: magit.info, Node: Point ends up inside invisible text when jumping to a file-visiting buffer, Next: I am no longer able to save popup defaults, Prev: git-commit-mode isn't used when committing from the command-line, Up: FAQ - Issues and Errors - -A.2.12 Point ends up inside invisible text when jumping to a file-visiting buffer ---------------------------------------------------------------------------------- - -This can happen when you type ‘RET’ on a hunk to visit the respective -file at the respective position. One solution to this problem is to use -‘global-reveal-mode’. It makes sure that text around point is always -visible. If that is too drastic for your taste, then you may instead -use ‘magit-diff-visit-file-hook’ to reveal the text, possibly using -‘reveal-post-command’ or for Org buffers ‘org-reveal’. - - -File: magit.info, Node: I am no longer able to save popup defaults, Prev: Point ends up inside invisible text when jumping to a file-visiting buffer, Up: FAQ - Issues and Errors - -A.2.13 I am no longer able to save popup defaults -------------------------------------------------- - -Magit used to use Magit-Popup to implement the transient popup menus. -Now it used Transient instead, which is Magit-Popup’s successor. - - In the older Magit-Popup menus, it was possible to save user settings -(e.g., setting the gpg signing key for commits) by using ‘C-c C-c’ in -the popup buffer. This would dismiss the popup, but save the settings -as the defaults for future popups. - - When switching to Transient menus, this functionality is now -available via ‘C-x C-s’ instead; the ‘C-x’ prefix has other options as -well when using Transient, which will be displayed when it is typed. -See <https://magit.vc/manual/transient/Saving-Values.html#Saving-Values> -for more details. - - -File: magit.info, Node: Debugging Tools, Next: Keystroke Index, Prev: FAQ, Up: Top - -B Debugging Tools -***************** - -Magit and its dependencies provide a few debugging tools, and we -appreciate it very much if you use those tools before reporting an -issue. Please include all relevant output when reporting an issue. - -‘M-x magit-version’ - This command shows the currently used versions of Magit, Git, and - Emacs in the echo area. Non-interactively this just returns the - Magit version. - -‘M-x magit-emacs-Q-command’ - This command shows a debugging shell command in the echo area and - adds it to the kill ring. Paste that command into a shell and run - it. - - This shell command starts ‘emacs’ with only ‘magit’ and its - dependencies loaded. Neither your configuration nor other - installed packages are loaded. This makes it easier to determine - whether some issue lays with Magit or something else. - - If you run Magit from its Git repository, then you should be able - to use ‘make emacs-Q’ instead of the output of this command. - -‘M-x magit-toggle-git-debug’ - This command toggles whether additional git errors are reported. - - Magit basically calls git for one of these two reasons: for - side-effects or to do something with its standard output. - - When git is run for side-effects then its output, including error - messages, go into the process buffer which is shown when using ‘$’. - - When git’s output is consumed in some way, then it would be too - expensive to also insert it into this buffer, but when this option - is non-nil and git returns with a non-zero exit status, then at - least its standard error is inserted into this buffer. - - This is only intended for debugging purposes. Do not enable this - permanently, that would negatively affect performance. Also note - that just because git exits with a non-zero exit status and prints - an error message that usually doesn’t mean that it is an error as - far as Magit is concerned, which is another reason we usually hide - these error messages. Whether some error message is relevant in - the context of some unexpected behavior has to be judged on a case - by case basis. - -‘M-x magit-toggle-verbose-refresh’ - This command toggles whether Magit refreshes buffers verbosely. - Enabling this helps figuring out which sections are bottlenecks. - The additional output can be found in the ‘*Messages*’ buffer. - -‘M-x magit-debug-git-executable’ - This command displays a buffer containing information about the - available and used ‘git’ executable(s), and can be useful when - investigating ‘exec-path’ issues. - - Also see *note Git Executable::. - -‘M-x with-editor-debug’ - This command displays a buffer containing information about the - available and used ‘emacsclient’ executable(s), and can be useful - when investigating why Magit (or rather ‘with-editor’) cannot find - an appropriate ‘emacsclient’ executable. - - Also see *note (with-editor)Debugging::. - -Please also see *note FAQ::. - - -File: magit.info, Node: Keystroke Index, Next: Function and Command Index, Prev: Debugging Tools, Up: Top - -Appendix C Keystroke Index -************************** - - -* Menu: - -* !: Running Git Manually. - (line 13) -* ! !: Running Git Manually. - (line 17) -* ! a: Running Git Manually. - (line 53) -* ! b: Running Git Manually. - (line 56) -* ! g: Running Git Manually. - (line 59) -* ! k: Running Git Manually. - (line 50) -* ! m: Running Git Manually. - (line 62) -* ! p: Running Git Manually. - (line 25) -* ! s: Running Git Manually. - (line 34) -* ! S: Running Git Manually. - (line 38) -* $: Viewing Git Output. (line 17) -* +: Log Buffer. (line 64) -* + <1>: Refreshing Diffs. (line 65) -* -: Log Buffer. (line 67) -* - <1>: Refreshing Diffs. (line 62) -* 0: Refreshing Diffs. (line 68) -* 1: Section Visibility. (line 39) -* 2: Section Visibility. (line 39) -* 3: Section Visibility. (line 39) -* 4: Section Visibility. (line 39) -* 5: Repository List. (line 115) -* :: Running Git Manually. - (line 25) -* =: Log Buffer. (line 59) -* >: Sparse checkouts. (line 17) -* > a: Sparse checkouts. (line 39) -* > d: Sparse checkouts. (line 50) -* > e: Sparse checkouts. (line 21) -* > r: Sparse checkouts. (line 44) -* > s: Sparse checkouts. (line 33) -* ^: Section Movement. (line 28) -* a: Applying. (line 34) -* A: Cherry Picking. (line 9) -* A A: Cherry Picking. (line 17) -* A a: Cherry Picking. (line 23) -* A A <1>: Cherry Picking. (line 85) -* A a <1>: Cherry Picking. (line 91) -* A d: Cherry Picking. (line 51) -* A h: Cherry Picking. (line 40) -* A n: Cherry Picking. (line 62) -* A s: Cherry Picking. (line 72) -* A s <1>: Cherry Picking. (line 88) -* B: Bisecting. (line 9) -* b: Blaming. (line 115) -* b <1>: Branch Commands. (line 13) -* b <2>: Editing Rebase Sequences. - (line 70) -* B B: Bisecting. (line 16) -* B b: Bisecting. (line 32) -* b b: Branch Commands. (line 47) -* b C: Branch Commands. (line 31) -* b c: Branch Commands. (line 63) -* B g: Bisecting. (line 36) -* B k: Bisecting. (line 46) -* b k: Branch Commands. (line 138) -* b l: Branch Commands. (line 69) -* B m: Bisecting. (line 40) -* b m: Branch Commands. (line 149) -* b n: Branch Commands. (line 54) -* B r: Bisecting. (line 51) -* B s: Bisecting. (line 26) -* b s: Branch Commands. (line 91) -* b S: Branch Commands. (line 118) -* b x: Branch Commands. (line 123) -* c: Blaming. (line 141) -* C: Cloning Repository. (line 20) -* c <1>: Initiating a Commit. (line 9) -* c <2>: Editing Rebase Sequences. - (line 59) -* C >: Cloning Repository. (line 38) -* c a: Initiating a Commit. (line 18) -* c A: Initiating a Commit. (line 59) -* C b: Cloning Repository. (line 44) -* C C: Cloning Repository. (line 28) -* c c: Initiating a Commit. (line 14) -* C d: Cloning Repository. (line 55) -* C e: Cloning Repository. (line 61) -* c e: Initiating a Commit. (line 21) -* c f: Initiating a Commit. (line 39) -* c F: Initiating a Commit. (line 46) -* C m: Cloning Repository. (line 48) -* C s: Cloning Repository. (line 32) -* c s: Initiating a Commit. (line 49) -* c S: Initiating a Commit. (line 56) -* c w: Initiating a Commit. (line 30) -* C-<return>: Visiting Files and Blobs from a Diff. - (line 50) -* C-<tab>: Section Visibility. (line 14) -* C-c C-a: Commit Pseudo Headers. - (line 16) -* C-c C-b: Log Buffer. (line 20) -* C-c C-b <1>: Refreshing Diffs. (line 84) -* C-c C-c: Select from Log. (line 21) -* C-c C-c <1>: Editing Commit Messages. - (line 18) -* C-c C-c <2>: Editing Rebase Sequences. - (line 7) -* C-c C-d: Refreshing Diffs. (line 75) -* C-c C-d <1>: Editing Commit Messages. - (line 54) -* C-c C-e: Commands Available in Diffs. - (line 24) -* C-c C-f: Log Buffer. (line 23) -* C-c C-f <1>: Refreshing Diffs. (line 87) -* C-c C-i: Commit Pseudo Headers. - (line 13) -* C-c C-k: Select from Log. (line 26) -* C-c C-k <1>: Editing Commit Messages. - (line 22) -* C-c C-k <2>: Editing Rebase Sequences. - (line 11) -* C-c C-n: Log Buffer. (line 26) -* C-c C-o: Commit Pseudo Headers. - (line 28) -* C-c C-p: Commit Pseudo Headers. - (line 31) -* C-c C-r: Commit Pseudo Headers. - (line 19) -* C-c C-s: Commit Pseudo Headers. - (line 22) -* C-c C-t: Commands Available in Diffs. - (line 15) -* C-c C-t <1>: Commit Pseudo Headers. - (line 25) -* C-c C-w: Using the Revision Stack. - (line 7) -* C-c f: Commands for Buffers Visiting Files. - (line 52) -* C-c f , c: Commands for Buffers Visiting Files. - (line 52) -* C-c f , k: Commands for Buffers Visiting Files. - (line 52) -* C-c f , r: Commands for Buffers Visiting Files. - (line 52) -* C-c f , x: Commands for Buffers Visiting Files. - (line 52) -* C-c f B: Blaming. (line 28) -* C-c f b: Blaming. (line 28) -* C-c f B <1>: Commands for Buffers Visiting Files. - (line 52) -* C-c f b <1>: Commands for Buffers Visiting Files. - (line 52) -* C-c f B b: Blaming. (line 28) -* C-c f B e: Blaming. (line 28) -* C-c f B f: Blaming. (line 28) -* C-c f B q: Blaming. (line 28) -* C-c f B r: Blaming. (line 28) -* C-c f c: Commands for Buffers Visiting Files. - (line 52) -* C-c f D: Commands for Buffers Visiting Files. - (line 52) -* C-c f d: Commands for Buffers Visiting Files. - (line 52) -* C-c f e: Blaming. (line 28) -* C-c f e <1>: Commands for Buffers Visiting Files. - (line 52) -* C-c f f: Blaming. (line 28) -* C-c f f <1>: Commands for Buffers Visiting Files. - (line 52) -* C-c f g: Commands for Buffers Visiting Files. - (line 52) -* C-c f G: Commands for Buffers Visiting Files. - (line 52) -* C-c f L: Commands for Buffers Visiting Files. - (line 52) -* C-c f l: Commands for Buffers Visiting Files. - (line 52) -* C-c f M: Commands for Buffers Visiting Files. - (line 52) -* C-c f m: Commands for Buffers Visiting Files. - (line 52) -* C-c f n: Commands for Buffers Visiting Files. - (line 52) -* C-c f p: Commands for Buffers Visiting Files. - (line 52) -* C-c f q: Blaming. (line 28) -* C-c f q <1>: Commands for Buffers Visiting Files. - (line 52) -* C-c f r: Blaming. (line 28) -* C-c f r <1>: Commands for Buffers Visiting Files. - (line 52) -* C-c f s: Commands for Buffers Visiting Files. - (line 52) -* C-c f s <1>: Commands for Buffers Visiting Files. - (line 52) -* C-c f t: Commands for Buffers Visiting Files. - (line 52) -* C-c f u: Commands for Buffers Visiting Files. - (line 52) -* C-c f u <1>: Commands for Buffers Visiting Files. - (line 52) -* C-c f v: Commands for Buffers Visiting Files. - (line 52) -* C-c f V: Commands for Buffers Visiting Files. - (line 52) -* C-c g: Transient Commands. (line 20) -* C-c M-g: Commands for Buffers Visiting Files. - (line 58) -* C-c M-g , c: Commands for Buffers Visiting Files. - (line 86) -* C-c M-g , k: Commands for Buffers Visiting Files. - (line 82) -* C-c M-g , r: Commands for Buffers Visiting Files. - (line 78) -* C-c M-g , x: Commands for Buffers Visiting Files. - (line 74) -* C-c M-g B: Blaming. (line 34) -* C-c M-g b: Blaming. (line 45) -* C-c M-g B <1>: Commands for Buffers Visiting Files. - (line 137) -* C-c M-g B b: Blaming. (line 45) -* C-c M-g B e: Blaming. (line 76) -* C-c M-g B f: Blaming. (line 68) -* C-c M-g B q: Blaming. (line 87) -* C-c M-g B r: Blaming. (line 60) -* C-c M-g c: Commands for Buffers Visiting Files. - (line 176) -* C-c M-g D: Commands for Buffers Visiting Files. - (line 91) -* C-c M-g d: Commands for Buffers Visiting Files. - (line 101) -* C-c M-g e: Blaming. (line 76) -* C-c M-g e <1>: Commands for Buffers Visiting Files. - (line 182) -* C-c M-g f: Blaming. (line 68) -* C-c M-g g: Commands for Buffers Visiting Files. - (line 166) -* C-c M-g G: Commands for Buffers Visiting Files. - (line 172) -* C-c M-g L: Commands for Buffers Visiting Files. - (line 109) -* C-c M-g l: Commands for Buffers Visiting Files. - (line 119) -* C-c M-g M: Commands for Buffers Visiting Files. - (line 132) -* C-c M-g n: Commands for Buffers Visiting Files. - (line 153) -* C-c M-g p: Commands for Buffers Visiting Files. - (line 149) -* C-c M-g q: Blaming. (line 87) -* C-c M-g r: Blaming. (line 60) -* C-c M-g s: Commands for Buffers Visiting Files. - (line 63) -* C-c M-g s <1>: Commands for Buffers Visiting Files. - (line 63) -* C-c M-g t: Commands for Buffers Visiting Files. - (line 129) -* C-c M-g u: Commands for Buffers Visiting Files. - (line 69) -* C-c M-g u <1>: Commands for Buffers Visiting Files. - (line 69) -* C-c M-g v: Commands for Buffers Visiting Files. - (line 156) -* C-c M-g V: Commands for Buffers Visiting Files. - (line 160) -* C-c M-i: Commit Pseudo Headers. - (line 35) -* C-c M-s: Editing Commit Messages. - (line 33) -* C-c TAB: Section Visibility. (line 14) -* C-w: Common Commands. (line 22) -* C-x g: Status Buffer. (line 23) -* C-x M-g: Transient Commands. (line 20) -* C-x u: Editing Rebase Sequences. - (line 77) -* d: Diffing. (line 22) -* D: Refreshing Diffs. (line 16) -* d c: Diffing. (line 63) -* d d: Diffing. (line 27) -* D f: Refreshing Diffs. (line 45) -* D F: Refreshing Diffs. (line 49) -* D g: Refreshing Diffs. (line 21) -* d p: Diffing. (line 56) -* d r: Diffing. (line 30) -* D r: Refreshing Diffs. (line 41) -* d s: Diffing. (line 48) -* D s: Refreshing Diffs. (line 25) -* d t: Diffing. (line 67) -* D t: Refreshing Diffs. (line 38) -* d u: Diffing. (line 53) -* d w: Diffing. (line 43) -* D w: Refreshing Diffs. (line 31) -* DEL: Log Buffer. (line 50) -* DEL <1>: Commands Available in Diffs. - (line 56) -* DEL <2>: Blaming. (line 103) -* DEL <3>: Editing Rebase Sequences. - (line 25) -* e: Ediffing. (line 10) -* E: Ediffing. (line 21) -* e <1>: Editing Rebase Sequences. - (line 46) -* E c: Ediffing. (line 100) -* E i: Ediffing. (line 94) -* E m: Ediffing. (line 33) -* E M: Ediffing. (line 48) -* E r: Ediffing. (line 25) -* E s: Ediffing. (line 87) -* E t: Ediffing. (line 79) -* E u: Ediffing. (line 91) -* E w: Ediffing. (line 97) -* E z: Ediffing. (line 103) -* f: Repository List. (line 111) -* f <1>: Editing Rebase Sequences. - (line 52) -* f <2>: Fetching. (line 10) -* F: Pulling. (line 10) -* f a: Fetching. (line 45) -* f C: Branch Commands. (line 31) -* F C: Branch Commands. (line 31) -* f e: Fetching. (line 34) -* F e: Pulling. (line 28) -* f m: Fetching. (line 48) -* f o: Fetching. (line 37) -* f p: Fetching. (line 15) -* F p: Pulling. (line 14) -* f r: Fetching. (line 41) -* f u: Fetching. (line 22) -* F u: Pulling. (line 21) -* g: Automatic Refreshing of Magit Buffers. - (line 26) -* G: Automatic Refreshing of Magit Buffers. - (line 34) -* H: Section Types and Values. - (line 14) -* I: Creating Repository. (line 7) -* j: Log Buffer. (line 31) -* j <1>: Commands Available in Diffs. - (line 43) -* k: Viewing Git Output. (line 24) -* k <1>: Applying. (line 40) -* k <2>: Editing Rebase Sequences. - (line 56) -* k <3>: Stashing. (line 118) -* l: Logging. (line 30) -* L: Refreshing Logs. (line 12) -* L <1>: Log Buffer. (line 7) -* L <2>: Log Margin. (line 52) -* l <1>: Editing Rebase Sequences. - (line 94) -* l a: Logging. (line 61) -* l b: Logging. (line 58) -* L d: Log Margin. (line 66) -* L g: Refreshing Logs. (line 17) -* l h: Logging. (line 40) -* l H: Reflog. (line 18) -* l l: Logging. (line 35) -* l L: Logging. (line 55) -* L L: Refreshing Logs. (line 34) -* L L <1>: Log Margin. (line 60) -* L l: Log Margin. (line 63) -* l o: Logging. (line 49) -* l O: Reflog. (line 15) -* l r: Reflog. (line 12) -* L s: Refreshing Logs. (line 21) -* l u: Logging. (line 43) -* L w: Refreshing Logs. (line 27) -* m: Repository List. (line 105) -* m <1>: Merging. (line 10) -* M: Remote Commands. (line 14) -* m a: Merging. (line 42) -* m a <1>: Merging. (line 91) -* M a: Remote Commands. (line 48) -* M C: Remote Commands. (line 32) -* m e: Merging. (line 30) -* m i: Merging. (line 54) -* M k: Remote Commands. (line 60) -* m m: Merging. (line 18) -* m m <1>: Merging. (line 86) -* m n: Merging. (line 36) -* m p: Merging. (line 75) -* M p: Remote Commands. (line 63) -* M P: Remote Commands. (line 67) -* M r: Remote Commands. (line 52) -* m s: Merging. (line 67) -* M u: Remote Commands. (line 56) -* M-1: Section Visibility. (line 45) -* M-2: Section Visibility. (line 45) -* M-3: Section Visibility. (line 45) -* M-4: Section Visibility. (line 45) -* M-<tab>: Section Visibility. (line 29) -* M-n: Section Movement. (line 24) -* M-n <1>: Editing Commit Messages. - (line 41) -* M-n <2>: Editing Rebase Sequences. - (line 40) -* M-p: Section Movement. (line 19) -* M-p <1>: Editing Commit Messages. - (line 36) -* M-p <2>: Editing Rebase Sequences. - (line 37) -* M-w: Blaming. (line 134) -* M-w <1>: Common Commands. (line 39) -* MM: Editing Rebase Sequences. - (line 102) -* Mt: Editing Rebase Sequences. - (line 108) -* n: Section Movement. (line 16) -* n <1>: Blaming. (line 118) -* N: Blaming. (line 121) -* n <2>: Editing Rebase Sequences. - (line 34) -* n <3>: Minor Mode for Buffers Visiting Blobs. - (line 16) -* o: Submodule Transient. (line 7) -* O: Subtree. (line 9) -* o a: Submodule Transient. (line 20) -* o d: Submodule Transient. (line 45) -* O e: Subtree. (line 37) -* O e p: Subtree. (line 48) -* O e s: Subtree. (line 52) -* o f: Submodule Transient. (line 51) -* O i: Subtree. (line 13) -* O i a: Subtree. (line 24) -* O i c: Subtree. (line 28) -* O i f: Subtree. (line 34) -* O i m: Subtree. (line 31) -* o l: Submodule Transient. (line 48) -* o p: Submodule Transient. (line 32) -* o r: Submodule Transient. (line 26) -* o s: Submodule Transient. (line 40) -* o u: Submodule Transient. (line 36) -* p: Section Movement. (line 11) -* p <1>: Blaming. (line 124) -* P: Blaming. (line 127) -* p <2>: Editing Rebase Sequences. - (line 31) -* P <1>: Pushing. (line 10) -* p <3>: Minor Mode for Buffers Visiting Blobs. - (line 13) -* P C: Branch Commands. (line 31) -* P e: Pushing. (line 29) -* P m: Pushing. (line 45) -* P o: Pushing. (line 33) -* P p: Pushing. (line 15) -* P r: Pushing. (line 37) -* P t: Pushing. (line 52) -* P T: Pushing. (line 59) -* P u: Pushing. (line 22) -* q: Quitting Windows. (line 7) -* q <1>: Log Buffer. (line 14) -* q <2>: Blaming. (line 130) -* q <3>: Minor Mode for Buffers Visiting Blobs. - (line 19) -* r: Rebasing. (line 10) -* r <1>: Editing Rebase Sequences. - (line 43) -* r a: Rebasing. (line 111) -* r e: Rebasing. (line 42) -* r e <1>: Rebasing. (line 107) -* r f: Rebasing. (line 79) -* r i: Rebasing. (line 76) -* r k: Rebasing. (line 91) -* r m: Rebasing. (line 83) -* r p: Rebasing. (line 28) -* r r: Rebasing. (line 97) -* r s: Rebasing. (line 47) -* r s <1>: Rebasing. (line 103) -* r u: Rebasing. (line 35) -* r w: Rebasing. (line 87) -* RET: Repository List. (line 102) -* RET <1>: References Buffer. (line 159) -* RET <2>: Visiting Files and Blobs from a Diff. - (line 9) -* RET <3>: Blaming. (line 91) -* RET <4>: Editing Rebase Sequences. - (line 15) -* s: Staging and Unstaging. - (line 29) -* S: Staging and Unstaging. - (line 36) -* s <1>: Editing Rebase Sequences. - (line 49) -* S-<tab>: Section Visibility. (line 33) -* SPC: Log Buffer. (line 41) -* SPC <1>: Commands Available in Diffs. - (line 53) -* SPC <2>: Blaming. (line 94) -* SPC <3>: Editing Rebase Sequences. - (line 19) -* t: Editing Rebase Sequences. - (line 97) -* t <1>: Tagging. (line 9) -* T: Notes. (line 9) -* T a: Notes. (line 47) -* T c: Notes. (line 43) -* t k: Tagging. (line 37) -* T m: Notes. (line 35) -* t p: Tagging. (line 43) -* T p: Notes. (line 28) -* t r: Tagging. (line 18) -* T r: Notes. (line 21) -* t t: Tagging. (line 14) -* T T: Notes. (line 14) -* TAB: Section Visibility. (line 10) -* u: Repository List. (line 108) -* u <1>: Staging and Unstaging. - (line 42) -* U: Staging and Unstaging. - (line 50) -* v: Applying. (line 47) -* V: Reverting. (line 7) -* V a: Reverting. (line 35) -* V s: Reverting. (line 32) -* V V: Reverting. (line 15) -* V v: Reverting. (line 20) -* V V <1>: Reverting. (line 29) -* W: Plain Patches. (line 7) -* w: Maildir Patches. (line 9) -* w a: Plain Patches. (line 20) -* w a <1>: Maildir Patches. (line 23) -* w a <2>: Maildir Patches. (line 38) -* W c: Plain Patches. (line 12) -* w m: Maildir Patches. (line 20) -* W s: Plain Patches. (line 26) -* w s: Maildir Patches. (line 34) -* w w: Maildir Patches. (line 14) -* w w <1>: Maildir Patches. (line 31) -* x: Editing Rebase Sequences. - (line 62) -* x <1>: Resetting. (line 9) -* X f: Resetting. (line 44) -* X h: Resetting. (line 24) -* X i: Resetting. (line 33) -* X k: Resetting. (line 28) -* X m: Resetting. (line 15) -* X s: Resetting. (line 19) -* X w: Resetting. (line 39) -* X w <1>: Wip Modes. (line 64) -* Y: Cherries. (line 18) -* y: References Buffer. (line 7) -* y <1>: Editing Rebase Sequences. - (line 74) -* y c: References Buffer. (line 25) -* y o: References Buffer. (line 30) -* y r: References Buffer. (line 34) -* y y: References Buffer. (line 21) -* z: Stashing. (line 9) -* Z: Worktree. (line 9) -* z a: Stashing. (line 52) -* z b: Stashing. (line 105) -* z B: Stashing. (line 110) -* Z b: Worktree. (line 13) -* Z c: Worktree. (line 16) -* z f: Stashing. (line 115) -* Z g: Worktree. (line 26) -* z i: Stashing. (line 20) -* z I: Stashing. (line 42) -* z k: Stashing. (line 98) -* Z k: Worktree. (line 22) -* z l: Stashing. (line 121) -* Z m: Worktree. (line 19) -* z p: Stashing. (line 74) -* z v: Stashing. (line 102) -* z w: Stashing. (line 24) -* z W: Stashing. (line 46) -* z x: Stashing. (line 30) -* z z: Stashing. (line 14) -* z Z: Stashing. (line 36) - - -File: magit.info, Node: Function and Command Index, Next: Variable Index, Prev: Keystroke Index, Up: Top - -Appendix D Function and Command Index -************************************* - - -* Menu: - -* bug-reference-mode: Commit Mode and Hooks. - (line 48) -* forward-line: Editing Rebase Sequences. - (line 34) -* git-commit-ack: Commit Pseudo Headers. - (line 16) -* git-commit-cc: Commit Pseudo Headers. - (line 28) -* git-commit-check-style-conventions: Commit Message Conventions. - (line 33) -* git-commit-insert-pseudo-header: Commit Pseudo Headers. - (line 13) -* git-commit-next-message: Editing Commit Messages. - (line 41) -* git-commit-prev-message: Editing Commit Messages. - (line 36) -* git-commit-propertize-diff: Commit Mode and Hooks. - (line 40) -* git-commit-reported: Commit Pseudo Headers. - (line 31) -* git-commit-review: Commit Pseudo Headers. - (line 19) -* git-commit-save-message: Editing Commit Messages. - (line 33) -* git-commit-save-message <1>: Commit Mode and Hooks. - (line 26) -* git-commit-setup-changelog-support: Commit Mode and Hooks. - (line 29) -* git-commit-signoff: Commit Pseudo Headers. - (line 22) -* git-commit-suggested: Commit Pseudo Headers. - (line 35) -* git-commit-test: Commit Pseudo Headers. - (line 25) -* git-commit-turn-on-auto-fill: Commit Mode and Hooks. - (line 33) -* git-commit-turn-on-flyspell: Commit Mode and Hooks. - (line 36) -* git-rebase-backward-line: Editing Rebase Sequences. - (line 31) -* git-rebase-break: Editing Rebase Sequences. - (line 70) -* git-rebase-edit: Editing Rebase Sequences. - (line 46) -* git-rebase-exec: Editing Rebase Sequences. - (line 62) -* git-rebase-fixup: Editing Rebase Sequences. - (line 52) -* git-rebase-insert: Editing Rebase Sequences. - (line 74) -* git-rebase-kill-line: Editing Rebase Sequences. - (line 56) -* git-rebase-label: Editing Rebase Sequences. - (line 94) -* git-rebase-merge: Editing Rebase Sequences. - (line 102) -* git-rebase-merge-toggle-editmsg: Editing Rebase Sequences. - (line 108) -* git-rebase-move-line-down: Editing Rebase Sequences. - (line 40) -* git-rebase-move-line-up: Editing Rebase Sequences. - (line 37) -* git-rebase-pick: Editing Rebase Sequences. - (line 59) -* git-rebase-reset: Editing Rebase Sequences. - (line 97) -* git-rebase-reword: Editing Rebase Sequences. - (line 43) -* git-rebase-show-commit: Editing Rebase Sequences. - (line 15) -* git-rebase-show-or-scroll-down: Editing Rebase Sequences. - (line 25) -* git-rebase-show-or-scroll-up: Editing Rebase Sequences. - (line 19) -* git-rebase-squash: Editing Rebase Sequences. - (line 49) -* git-rebase-undo: Editing Rebase Sequences. - (line 77) -* ido-enter-magit-status: Status Buffer. (line 96) -* magit-add-section-hook: Section Hooks. (line 20) -* magit-after-save-refresh-status: Automatic Refreshing of Magit Buffers. - (line 55) -* magit-am: Maildir Patches. (line 9) -* magit-am-abort: Maildir Patches. (line 38) -* magit-am-apply-maildir: Maildir Patches. (line 20) -* magit-am-apply-patches: Maildir Patches. (line 14) -* magit-am-continue: Maildir Patches. (line 31) -* magit-am-skip: Maildir Patches. (line 34) -* magit-apply: Applying. (line 34) -* magit-bisect: Bisecting. (line 9) -* magit-bisect-bad: Bisecting. (line 32) -* magit-bisect-good: Bisecting. (line 36) -* magit-bisect-mark: Bisecting. (line 40) -* magit-bisect-reset: Bisecting. (line 51) -* magit-bisect-run: Bisecting. (line 26) -* magit-bisect-skip: Bisecting. (line 46) -* magit-bisect-start: Bisecting. (line 16) -* magit-blame: Blaming. (line 28) -* magit-blame <1>: Blaming. (line 34) -* magit-blame <2>: Blaming. (line 115) -* magit-blame <3>: Commands for Buffers Visiting Files. - (line 52) -* magit-blame <4>: Commands for Buffers Visiting Files. - (line 137) -* magit-blame-addition: Blaming. (line 28) -* magit-blame-addition <1>: Blaming. (line 45) -* magit-blame-additions: Commands for Buffers Visiting Files. - (line 52) -* magit-blame-copy-hash: Blaming. (line 134) -* magit-blame-cycle-style: Blaming. (line 141) -* magit-blame-echo: Blaming. (line 28) -* magit-blame-echo <1>: Blaming. (line 76) -* magit-blame-echo <2>: Commands for Buffers Visiting Files. - (line 52) -* magit-blame-next-chunk: Blaming. (line 118) -* magit-blame-next-chunk-same-commit: Blaming. (line 121) -* magit-blame-previous-chunk: Blaming. (line 124) -* magit-blame-previous-chunk-same-commit: Blaming. (line 127) -* magit-blame-quit: Blaming. (line 28) -* magit-blame-quit <1>: Blaming. (line 87) -* magit-blame-quit <2>: Blaming. (line 130) -* magit-blame-quit <3>: Commands for Buffers Visiting Files. - (line 52) -* magit-blame-removal: Blaming. (line 28) -* magit-blame-removal <1>: Blaming. (line 60) -* magit-blame-removal <2>: Commands for Buffers Visiting Files. - (line 52) -* magit-blame-reverse: Blaming. (line 28) -* magit-blame-reverse <1>: Blaming. (line 68) -* magit-blame-reverse <2>: Commands for Buffers Visiting Files. - (line 52) -* magit-blob-next: Commands for Buffers Visiting Files. - (line 52) -* magit-blob-next <1>: Commands for Buffers Visiting Files. - (line 153) -* magit-blob-next <2>: Minor Mode for Buffers Visiting Blobs. - (line 16) -* magit-blob-previous: Commands for Buffers Visiting Files. - (line 52) -* magit-blob-previous <1>: Commands for Buffers Visiting Files. - (line 149) -* magit-blob-previous <2>: Minor Mode for Buffers Visiting Blobs. - (line 13) -* magit-blob-visit-file: Commands for Buffers Visiting Files. - (line 52) -* magit-blob-visit-file <1>: Commands for Buffers Visiting Files. - (line 160) -* magit-branch: Branch Commands. (line 13) -* magit-branch-and-checkout: Branch Commands. (line 63) -* magit-branch-checkout: Branch Commands. (line 69) -* magit-branch-configure: Branch Commands. (line 31) -* magit-branch-create: Branch Commands. (line 54) -* magit-branch-delete: Branch Commands. (line 138) -* magit-branch-or-checkout: Branch Commands. (line 257) -* magit-branch-orphan: Branch Commands. (line 253) -* magit-branch-rename: Branch Commands. (line 149) -* magit-branch-reset: Branch Commands. (line 123) -* magit-branch-shelve: Auxiliary Branch Commands. - (line 9) -* magit-branch-spinoff: Branch Commands. (line 91) -* magit-branch-spinout: Branch Commands. (line 118) -* magit-branch-unshelve: Auxiliary Branch Commands. - (line 19) -* magit-builtin-completing-read: Support for Completion Frameworks. - (line 41) -* magit-bundle: Bundle. (line 8) -* magit-call-git: Calling Git for Effect. - (line 28) -* magit-call-process: Calling Git for Effect. - (line 31) -* magit-cancel-section: Creating Sections. (line 69) -* magit-checkout: Branch Commands. (line 47) -* magit-cherry: Cherries. (line 18) -* magit-cherry-apply: Cherry Picking. (line 23) -* magit-cherry-copy: Cherry Picking. (line 17) -* magit-cherry-donate: Cherry Picking. (line 51) -* magit-cherry-harvest: Cherry Picking. (line 40) -* magit-cherry-pick: Cherry Picking. (line 9) -* magit-cherry-spinoff: Cherry Picking. (line 72) -* magit-cherry-spinout: Cherry Picking. (line 62) -* magit-clone: Cloning Repository. (line 20) -* magit-clone-bare: Cloning Repository. (line 44) -* magit-clone-mirror: Cloning Repository. (line 48) -* magit-clone-regular: Cloning Repository. (line 28) -* magit-clone-shallow: Cloning Repository. (line 32) -* magit-clone-shallow-exclude: Cloning Repository. (line 61) -* magit-clone-shallow-since: Cloning Repository. (line 55) -* magit-clone-sparse: Cloning Repository. (line 38) -* magit-commit: Initiating a Commit. (line 9) -* magit-commit <1>: Commands for Buffers Visiting Files. - (line 52) -* magit-commit <2>: Commands for Buffers Visiting Files. - (line 176) -* magit-commit-amend: Initiating a Commit. (line 18) -* magit-commit-augment: Initiating a Commit. (line 59) -* magit-commit-create: Initiating a Commit. (line 14) -* magit-commit-extend: Initiating a Commit. (line 21) -* magit-commit-fixup: Initiating a Commit. (line 39) -* magit-commit-instant-fixup: Initiating a Commit. (line 46) -* magit-commit-instant-squash: Initiating a Commit. (line 56) -* magit-commit-reword: Initiating a Commit. (line 30) -* magit-commit-squash: Initiating a Commit. (line 49) -* magit-completing-read: Support for Completion Frameworks. - (line 57) -* magit-copy-buffer-revision: Common Commands. (line 39) -* magit-copy-section-value: Common Commands. (line 22) -* magit-current-section: Section Selection. (line 6) -* magit-cycle-margin-style: Log Margin. (line 63) -* magit-debug-git-executable: Git Executable. (line 55) -* magit-debug-git-executable <1>: Debugging Tools. (line 57) -* magit-define-section-jumper: Creating Sections. (line 74) -* magit-describe-section: Section Types and Values. - (line 14) -* magit-describe-section-briefly: Section Types and Values. - (line 17) -* magit-describe-section-briefly <1>: Matching Sections. (line 7) -* magit-diff: Diffing. (line 22) -* magit-diff <1>: Commands for Buffers Visiting Files. - (line 52) -* magit-diff <2>: Commands for Buffers Visiting Files. - (line 91) -* magit-diff-buffer-file: Commands for Buffers Visiting Files. - (line 52) -* magit-diff-buffer-file <1>: Commands for Buffers Visiting Files. - (line 101) -* magit-diff-default-context: Refreshing Diffs. (line 68) -* magit-diff-dwim: Diffing. (line 27) -* magit-diff-edit-hunk-commit: Commands Available in Diffs. - (line 24) -* magit-diff-flip-revs: Refreshing Diffs. (line 45) -* magit-diff-less-context: Refreshing Diffs. (line 62) -* magit-diff-more-context: Refreshing Diffs. (line 65) -* magit-diff-paths: Diffing. (line 56) -* magit-diff-range: Diffing. (line 30) -* magit-diff-refresh: Refreshing Diffs. (line 16) -* magit-diff-refresh <1>: Refreshing Diffs. (line 21) -* magit-diff-save-default-arguments: Refreshing Diffs. (line 31) -* magit-diff-scope: Matching Sections. (line 110) -* magit-diff-set-default-arguments: Refreshing Diffs. (line 25) -* magit-diff-show-or-scroll-down: Log Buffer. (line 50) -* magit-diff-show-or-scroll-down <1>: Blaming. (line 103) -* magit-diff-show-or-scroll-up: Log Buffer. (line 41) -* magit-diff-show-or-scroll-up <1>: Blaming. (line 94) -* magit-diff-staged: Diffing. (line 48) -* magit-diff-switch-range-type: Refreshing Diffs. (line 41) -* magit-diff-toggle-file-filter: Refreshing Diffs. (line 49) -* magit-diff-toggle-refine-hunk: Refreshing Diffs. (line 38) -* magit-diff-trace-definition: Commands Available in Diffs. - (line 15) -* magit-diff-type: Matching Sections. (line 88) -* magit-diff-unstaged: Diffing. (line 53) -* magit-diff-visit-file: Visiting Files and Blobs from a Diff. - (line 9) -* magit-diff-visit-file-other-frame: Visiting Files and Blobs from a Diff. - (line 71) -* magit-diff-visit-file-other-window: Visiting Files and Blobs from a Diff. - (line 70) -* magit-diff-visit-file-worktree: Visiting Files and Blobs from a Diff. - (line 50) -* magit-diff-visit-worktree-file-other-frame: Visiting Files and Blobs from a Diff. - (line 73) -* magit-diff-visit-worktree-file-other-window: Visiting Files and Blobs from a Diff. - (line 72) -* magit-diff-while-committing: Refreshing Diffs. (line 75) -* magit-diff-while-committing <1>: Editing Commit Messages. - (line 54) -* magit-diff-working-tree: Diffing. (line 43) -* magit-disable-section-inserter: Per-Repository Configuration. - (line 31) -* magit-discard: Applying. (line 40) -* magit-dispatch: Transient Commands. (line 20) -* magit-display-buffer: Switching Buffers. (line 6) -* magit-display-buffer-fullcolumn-most-v1: Switching Buffers. (line 68) -* magit-display-buffer-fullframe-status-topleft-v1: Switching Buffers. - (line 59) -* magit-display-buffer-fullframe-status-v1: Switching Buffers. - (line 54) -* magit-display-buffer-same-window-except-diff-v1: Switching Buffers. - (line 49) -* magit-display-buffer-traditional: Switching Buffers. (line 42) -* magit-display-repository-buffer: Common Commands. (line 9) -* magit-display-repository-buffer <1>: Commands for Buffers Visiting Files. - (line 52) -* magit-display-repository-buffer <2>: Commands for Buffers Visiting Files. - (line 172) -* magit-ediff: Ediffing. (line 21) -* magit-ediff-compare: Ediffing. (line 25) -* magit-ediff-dwim: Ediffing. (line 10) -* magit-ediff-resolve-all: Ediffing. (line 48) -* magit-ediff-resolve-rest: Ediffing. (line 33) -* magit-ediff-show-commit: Ediffing. (line 100) -* magit-ediff-show-staged: Ediffing. (line 94) -* magit-ediff-show-stash: Ediffing. (line 103) -* magit-ediff-show-unstaged: Ediffing. (line 91) -* magit-ediff-show-working-tree: Ediffing. (line 97) -* magit-ediff-stage: Ediffing. (line 87) -* magit-edit-line-commit: Commands for Buffers Visiting Files. - (line 52) -* magit-edit-line-commit <1>: Commands for Buffers Visiting Files. - (line 182) -* magit-emacs-Q-command: Debugging Tools. (line 16) -* magit-fetch: Fetching. (line 10) -* magit-fetch-all: Fetching. (line 45) -* magit-fetch-branch: Fetching. (line 37) -* magit-fetch-from-pushremote: Fetching. (line 15) -* magit-fetch-from-upstream: Fetching. (line 22) -* magit-fetch-modules: Fetching. (line 48) -* magit-fetch-modules <1>: Submodule Transient. (line 51) -* magit-fetch-other: Fetching. (line 34) -* magit-fetch-refspec: Fetching. (line 41) -* magit-file-checkout: Resetting. (line 44) -* magit-file-checkout <1>: Commands for Buffers Visiting Files. - (line 52) -* magit-file-checkout <2>: Commands for Buffers Visiting Files. - (line 86) -* magit-file-delete: Commands for Buffers Visiting Files. - (line 52) -* magit-file-delete <1>: Commands for Buffers Visiting Files. - (line 82) -* magit-file-dispatch: Commands for Buffers Visiting Files. - (line 52) -* magit-file-dispatch <1>: Commands for Buffers Visiting Files. - (line 58) -* magit-file-rename: Commands for Buffers Visiting Files. - (line 52) -* magit-file-rename <1>: Commands for Buffers Visiting Files. - (line 78) -* magit-file-untrack: Commands for Buffers Visiting Files. - (line 52) -* magit-file-untrack <1>: Commands for Buffers Visiting Files. - (line 74) -* magit-find-file: General-Purpose Visit Commands. - (line 9) -* magit-find-file <1>: Commands for Buffers Visiting Files. - (line 52) -* magit-find-file <2>: Commands for Buffers Visiting Files. - (line 156) -* magit-find-file-other-frame: General-Purpose Visit Commands. - (line 19) -* magit-find-file-other-window: General-Purpose Visit Commands. - (line 14) -* magit-generate-buffer-name-default-function: Naming Buffers. - (line 16) -* magit-get-section: Matching Sections. (line 14) -* magit-git: Calling Git for Effect. - (line 46) -* magit-git-command: Running Git Manually. - (line 25) -* magit-git-command-topdir: Running Git Manually. - (line 17) -* magit-git-exit-code: Getting a Value from Git. - (line 10) -* magit-git-failure: Getting a Value from Git. - (line 17) -* magit-git-false: Getting a Value from Git. - (line 25) -* magit-git-insert: Getting a Value from Git. - (line 29) -* magit-git-items: Getting a Value from Git. - (line 41) -* magit-git-lines: Getting a Value from Git. - (line 37) -* magit-git-mergetool: Running Git Manually. - (line 62) -* magit-git-mergetool <1>: Ediffing. (line 79) -* magit-git-str: Getting a Value from Git. - (line 75) -* magit-git-string: Getting a Value from Git. - (line 32) -* magit-git-success: Getting a Value from Git. - (line 13) -* magit-git-true: Getting a Value from Git. - (line 21) -* magit-git-wash: Calling Git for Effect. - (line 50) -* magit-go-backward: Log Buffer. (line 20) -* magit-go-backward <1>: Refreshing Diffs. (line 84) -* magit-go-forward: Log Buffer. (line 23) -* magit-go-forward <1>: Refreshing Diffs. (line 87) -* magit-hunk-set-window-start: Section Movement. (line 45) -* magit-ido-completing-read: Support for Completion Frameworks. - (line 46) -* magit-init: Creating Repository. (line 7) -* magit-insert-am-sequence: Status Sections. (line 25) -* magit-insert-assumed-unchanged-files: Status Sections. (line 98) -* magit-insert-bisect-log: Status Sections. (line 39) -* magit-insert-bisect-output: Status Sections. (line 33) -* magit-insert-bisect-rest: Status Sections. (line 36) -* magit-insert-diff-filter-header: Status Header Sections. - (line 35) -* magit-insert-error-header: Status Header Sections. - (line 26) -* magit-insert-head-branch-header: Status Header Sections. - (line 38) -* magit-insert-heading: Creating Sections. (line 41) -* magit-insert-ignored-files: Status Sections. (line 83) -* magit-insert-local-branches: References Sections. (line 16) -* magit-insert-merge-log: Status Sections. (line 17) -* magit-insert-modules: Status Module Sections. - (line 12) -* magit-insert-modules-overview: Status Module Sections. - (line 30) -* magit-insert-modules-unpulled-from-pushremote: Status Module Sections. - (line 45) -* magit-insert-modules-unpulled-from-upstream: Status Module Sections. - (line 40) -* magit-insert-modules-unpushed-to-pushremote: Status Module Sections. - (line 55) -* magit-insert-modules-unpushed-to-upstream: Status Module Sections. - (line 50) -* magit-insert-push-branch-header: Status Header Sections. - (line 45) -* magit-insert-rebase-sequence: Status Sections. (line 21) -* magit-insert-recent-commits: Status Sections. (line 110) -* magit-insert-remote-branches: References Sections. (line 19) -* magit-insert-remote-header: Status Header Sections. - (line 58) -* magit-insert-repo-header: Status Header Sections. - (line 55) -* magit-insert-section: Creating Sections. (line 6) -* magit-insert-sequencer-sequence: Status Sections. (line 29) -* magit-insert-skip-worktree-files: Status Sections. (line 92) -* magit-insert-staged-changes: Status Sections. (line 53) -* magit-insert-stashes: Status Sections. (line 56) -* magit-insert-status-headers: Status Header Sections. - (line 12) -* magit-insert-tags: References Sections. (line 22) -* magit-insert-tags-header: Status Header Sections. - (line 49) -* magit-insert-tracked-files: Status Sections. (line 80) -* magit-insert-unpulled-cherries: Status Sections. (line 119) -* magit-insert-unpulled-from-pushremote: Status Sections. (line 66) -* magit-insert-unpulled-from-upstream: Status Sections. (line 62) -* magit-insert-unpulled-or-recent-commits: Status Sections. (line 104) -* magit-insert-unpushed-cherries: Status Sections. (line 125) -* magit-insert-unpushed-to-pushremote: Status Sections. (line 74) -* magit-insert-unpushed-to-upstream: Status Sections. (line 70) -* magit-insert-unstaged-changes: Status Sections. (line 50) -* magit-insert-untracked-files: Status Sections. (line 42) -* magit-insert-upstream-branch-header: Status Header Sections. - (line 41) -* magit-insert-user-header: Status Header Sections. - (line 65) -* magit-jump-to-diffstat-or-diff: Commands Available in Diffs. - (line 43) -* magit-kill-this-buffer: Minor Mode for Buffers Visiting Blobs. - (line 19) -* magit-list-repositories: Repository List. (line 6) -* magit-list-submodules: Listing Submodules. (line 13) -* magit-list-submodules <1>: Submodule Transient. (line 48) -* magit-log: Logging. (line 30) -* magit-log <1>: Commands for Buffers Visiting Files. - (line 52) -* magit-log <2>: Commands for Buffers Visiting Files. - (line 109) -* magit-log-all: Logging. (line 61) -* magit-log-all-branches: Logging. (line 58) -* magit-log-branches: Logging. (line 55) -* magit-log-buffer-file: Commands for Buffers Visiting Files. - (line 52) -* magit-log-buffer-file <1>: Commands for Buffers Visiting Files. - (line 119) -* magit-log-bury-buffer: Log Buffer. (line 14) -* magit-log-current: Logging. (line 35) -* magit-log-double-commit-limit: Log Buffer. (line 64) -* magit-log-half-commit-limit: Log Buffer. (line 67) -* magit-log-head: Logging. (line 40) -* magit-log-maybe-show-more-commits: Section Movement. (line 58) -* magit-log-maybe-update-blob-buffer: Section Movement. (line 72) -* magit-log-maybe-update-revision-buffer: Section Movement. (line 65) -* magit-log-merged: Commands for Buffers Visiting Files. - (line 52) -* magit-log-merged <1>: Commands for Buffers Visiting Files. - (line 132) -* magit-log-move-to-parent: Log Buffer. (line 26) -* magit-log-move-to-revision: Log Buffer. (line 31) -* magit-log-other: Logging. (line 49) -* magit-log-refresh: Refreshing Logs. (line 12) -* magit-log-refresh <1>: Refreshing Logs. (line 17) -* magit-log-refresh <2>: Log Buffer. (line 7) -* magit-log-related: Logging. (line 43) -* magit-log-save-default-arguments: Refreshing Logs. (line 27) -* magit-log-select-pick: Select from Log. (line 21) -* magit-log-select-quit: Select from Log. (line 26) -* magit-log-set-default-arguments: Refreshing Logs. (line 21) -* magit-log-toggle-commit-limit: Log Buffer. (line 59) -* magit-log-trace-definition: Commands for Buffers Visiting Files. - (line 52) -* magit-log-trace-definition <1>: Commands for Buffers Visiting Files. - (line 129) -* magit-margin-settings: Log Margin. (line 52) -* magit-maybe-set-dedicated: Switching Buffers. (line 89) -* magit-merge: Merging. (line 10) -* magit-merge <1>: Merging. (line 86) -* magit-merge-abort: Merging. (line 91) -* magit-merge-absorb: Merging. (line 42) -* magit-merge-editmsg: Merging. (line 30) -* magit-merge-into: Merging. (line 54) -* magit-merge-nocommit: Merging. (line 36) -* magit-merge-plain: Merging. (line 18) -* magit-merge-preview: Merging. (line 75) -* magit-merge-squash: Merging. (line 67) -* magit-mode-bury-buffer: Quitting Windows. (line 7) -* magit-mode-display-buffer: Refreshing Buffers. (line 32) -* magit-mode-quit-window: Quitting Windows. (line 34) -* magit-mode-setup: Refreshing Buffers. (line 17) -* magit-notes: Notes. (line 9) -* magit-notes-edit: Notes. (line 14) -* magit-notes-merge: Notes. (line 35) -* magit-notes-merge-abort: Notes. (line 47) -* magit-notes-merge-commit: Notes. (line 43) -* magit-notes-prune: Notes. (line 28) -* magit-notes-remove: Notes. (line 21) -* magit-patch: Plain Patches. (line 7) -* magit-patch-apply: Plain Patches. (line 20) -* magit-patch-apply <1>: Maildir Patches. (line 23) -* magit-patch-create: Plain Patches. (line 12) -* magit-patch-save: Plain Patches. (line 26) -* magit-pop-revision-stack: Using the Revision Stack. - (line 7) -* magit-process: Viewing Git Output. (line 17) -* magit-process-file: Getting a Value from Git. - (line 57) -* magit-process-git: Getting a Value from Git. - (line 50) -* magit-process-kill: Viewing Git Output. (line 24) -* magit-pull: Pulling. (line 10) -* magit-pull-branch: Pulling. (line 28) -* magit-pull-from-pushremote: Pulling. (line 14) -* magit-pull-from-upstream: Pulling. (line 21) -* magit-push: Pushing. (line 10) -* magit-push-current: Pushing. (line 29) -* magit-push-current-to-pushremote: Pushing. (line 15) -* magit-push-current-to-upstream: Pushing. (line 22) -* magit-push-implicitly: Pushing. (line 74) -* magit-push-matching: Pushing. (line 45) -* magit-push-other: Pushing. (line 33) -* magit-push-refspecs: Pushing. (line 37) -* magit-push-tag: Pushing. (line 59) -* magit-push-tags: Pushing. (line 52) -* magit-push-to-remote: Pushing. (line 91) -* magit-rebase: Rebasing. (line 10) -* magit-rebase-abort: Rebasing. (line 111) -* magit-rebase-autosquash: Rebasing. (line 79) -* magit-rebase-branch: Rebasing. (line 42) -* magit-rebase-continue: Rebasing. (line 97) -* magit-rebase-edit: Rebasing. (line 107) -* magit-rebase-edit-commit: Rebasing. (line 83) -* magit-rebase-interactive: Rebasing. (line 76) -* magit-rebase-onto-pushremote: Rebasing. (line 28) -* magit-rebase-onto-upstream: Rebasing. (line 35) -* magit-rebase-remove-commit: Rebasing. (line 91) -* magit-rebase-reword-commit: Rebasing. (line 87) -* magit-rebase-skip: Rebasing. (line 103) -* magit-rebase-subset: Rebasing. (line 47) -* magit-reflog-current: Reflog. (line 12) -* magit-reflog-head: Reflog. (line 18) -* magit-reflog-other: Reflog. (line 15) -* magit-refresh: Automatic Refreshing of Magit Buffers. - (line 26) -* magit-refresh-all: Automatic Refreshing of Magit Buffers. - (line 34) -* magit-refs-set-show-commit-count: References Buffer. (line 34) -* magit-region-sections: Section Selection. (line 9) -* magit-region-values: Section Selection. (line 35) -* magit-remote: Remote Commands. (line 14) -* magit-remote-add: Remote Commands. (line 48) -* magit-remote-configure: Remote Commands. (line 32) -* magit-remote-prune: Remote Commands. (line 63) -* magit-remote-prune-refspecs: Remote Commands. (line 67) -* magit-remote-remove: Remote Commands. (line 60) -* magit-remote-rename: Remote Commands. (line 52) -* magit-remote-set-url: Remote Commands. (line 56) -* magit-repolist-column-branch: Repository List. (line 51) -* magit-repolist-column-branches: Repository List. (line 58) -* magit-repolist-column-flag: Repository List. (line 64) -* magit-repolist-column-flags: Repository List. (line 76) -* magit-repolist-column-ident: Repository List. (line 40) -* magit-repolist-column-path: Repository List. (line 44) -* magit-repolist-column-stashes: Repository List. (line 61) -* magit-repolist-column-unpulled-from-pushremote: Repository List. - (line 87) -* magit-repolist-column-unpulled-from-upstream: Repository List. - (line 83) -* magit-repolist-column-unpushed-to-pushremote: Repository List. - (line 95) -* magit-repolist-column-unpushed-to-upstream: Repository List. - (line 91) -* magit-repolist-column-upstream: Repository List. (line 54) -* magit-repolist-column-version: Repository List. (line 47) -* magit-repolist-fetch: Repository List. (line 111) -* magit-repolist-find-file-other-frame: Repository List. (line 115) -* magit-repolist-mark: Repository List. (line 105) -* magit-repolist-status: Repository List. (line 102) -* magit-repolist-unmark: Repository List. (line 108) -* magit-reset-hard: Resetting. (line 24) -* magit-reset-index: Staging and Unstaging. - (line 78) -* magit-reset-index <1>: Resetting. (line 33) -* magit-reset-keep: Resetting. (line 28) -* magit-reset-mixed: Resetting. (line 15) -* magit-reset-quickly: Resetting. (line 9) -* magit-reset-soft: Resetting. (line 19) -* magit-reset-worktree: Resetting. (line 39) -* magit-reset-worktree <1>: Wip Modes. (line 64) -* magit-restore-window-configuration: Quitting Windows. (line 24) -* magit-reverse: Applying. (line 47) -* magit-reverse-in-index: Staging and Unstaging. - (line 58) -* magit-revert: Reverting. (line 7) -* magit-revert-and-commit: Reverting. (line 15) -* magit-revert-no-commit: Reverting. (line 20) -* magit-run: Running Git Manually. - (line 13) -* magit-run-git: Calling Git for Effect. - (line 34) -* magit-run-git-async: Calling Git for Effect. - (line 59) -* magit-run-git-gui: Running Git Manually. - (line 59) -* magit-run-git-with-editor: Calling Git for Effect. - (line 71) -* magit-run-git-with-input: Calling Git for Effect. - (line 37) -* magit-run-gitk: Running Git Manually. - (line 50) -* magit-run-gitk-all: Running Git Manually. - (line 53) -* magit-run-gitk-branches: Running Git Manually. - (line 56) -* magit-save-window-configuration: Switching Buffers. (line 80) -* magit-section-backward: Section Movement. (line 11) -* magit-section-backward-siblings: Section Movement. (line 19) -* magit-section-case: Matching Sections. (line 66) -* magit-section-cycle: Section Visibility. (line 14) -* magit-section-cycle-diffs: Section Visibility. (line 29) -* magit-section-cycle-global: Section Visibility. (line 33) -* magit-section-forward: Section Movement. (line 16) -* magit-section-forward-siblings: Section Movement. (line 24) -* magit-section-hide: Section Visibility. (line 55) -* magit-section-hide-children: Section Visibility. (line 67) -* magit-section-ident: Matching Sections. (line 10) -* magit-section-match: Matching Sections. (line 18) -* magit-section-set-window-start: Section Movement. (line 52) -* magit-section-show: Section Visibility. (line 52) -* magit-section-show-children: Section Visibility. (line 62) -* magit-section-show-headings: Section Visibility. (line 58) -* magit-section-show-level-1: Section Visibility. (line 39) -* magit-section-show-level-1-all: Section Visibility. (line 45) -* magit-section-show-level-2: Section Visibility. (line 39) -* magit-section-show-level-2-all: Section Visibility. (line 45) -* magit-section-show-level-3: Section Visibility. (line 39) -* magit-section-show-level-3-all: Section Visibility. (line 45) -* magit-section-show-level-4: Section Visibility. (line 39) -* magit-section-show-level-4-all: Section Visibility. (line 45) -* magit-section-toggle: Section Visibility. (line 10) -* magit-section-toggle-children: Section Visibility. (line 70) -* magit-section-up: Section Movement. (line 28) -* magit-section-value-if: Matching Sections. (line 57) -* magit-sequence-abort: Cherry Picking. (line 91) -* magit-sequence-abort <1>: Reverting. (line 35) -* magit-sequence-continue: Cherry Picking. (line 85) -* magit-sequence-continue <1>: Reverting. (line 29) -* magit-sequence-skip: Cherry Picking. (line 88) -* magit-sequence-skip <1>: Reverting. (line 32) -* magit-shell-command: Running Git Manually. - (line 38) -* magit-shell-command-topdir: Running Git Manually. - (line 34) -* magit-show-commit: Diffing. (line 63) -* magit-show-commit <1>: Blaming. (line 91) -* magit-show-refs: References Buffer. (line 7) -* magit-show-refs-current: References Buffer. (line 25) -* magit-show-refs-head: References Buffer. (line 21) -* magit-show-refs-other: References Buffer. (line 30) -* magit-snapshot-both: Stashing. (line 36) -* magit-snapshot-index: Stashing. (line 42) -* magit-snapshot-worktree: Stashing. (line 46) -* magit-sparse-checkout: Sparse checkouts. (line 17) -* magit-sparse-checkout-add: Sparse checkouts. (line 39) -* magit-sparse-checkout-disable: Sparse checkouts. (line 50) -* magit-sparse-checkout-enable: Sparse checkouts. (line 21) -* magit-sparse-checkout-reapply: Sparse checkouts. (line 44) -* magit-sparse-checkout-set: Sparse checkouts. (line 33) -* magit-stage: Staging and Unstaging. - (line 29) -* magit-stage-buffer-file: Commands for Buffers Visiting Files. - (line 52) -* magit-stage-buffer-file <1>: Commands for Buffers Visiting Files. - (line 63) -* magit-stage-file: Staging from File-Visiting Buffers. - (line 11) -* magit-stage-file <1>: Commands for Buffers Visiting Files. - (line 52) -* magit-stage-file <2>: Commands for Buffers Visiting Files. - (line 63) -* magit-stage-modified: Staging and Unstaging. - (line 36) -* magit-start-git: Calling Git for Effect. - (line 82) -* magit-start-process: Calling Git for Effect. - (line 100) -* magit-stash: Stashing. (line 9) -* magit-stash-apply: Stashing. (line 52) -* magit-stash-both: Stashing. (line 14) -* magit-stash-branch: Stashing. (line 105) -* magit-stash-branch-here: Stashing. (line 110) -* magit-stash-clear: Stashing. (line 118) -* magit-stash-drop: Stashing. (line 98) -* magit-stash-format-patch: Stashing. (line 115) -* magit-stash-index: Stashing. (line 20) -* magit-stash-keep-index: Stashing. (line 30) -* magit-stash-list: Stashing. (line 121) -* magit-stash-pop: Stashing. (line 74) -* magit-stash-show: Diffing. (line 67) -* magit-stash-show <1>: Stashing. (line 102) -* magit-stash-worktree: Stashing. (line 24) -* magit-stashes-maybe-update-stash-buffer: Section Movement. (line 92) -* magit-status: Status Buffer. (line 23) -* magit-status-here: Commands for Buffers Visiting Files. - (line 52) -* magit-status-here <1>: Commands for Buffers Visiting Files. - (line 166) -* magit-status-maybe-update-blob-buffer: Section Movement. (line 87) -* magit-status-maybe-update-revision-buffer: Section Movement. - (line 77) -* magit-status-maybe-update-stash-buffer: Section Movement. (line 82) -* magit-status-quick: Status Buffer. (line 70) -* magit-submodule: Submodule Transient. (line 7) -* magit-submodule-add: Submodule Transient. (line 20) -* magit-submodule-populate: Submodule Transient. (line 32) -* magit-submodule-register: Submodule Transient. (line 26) -* magit-submodule-synchronize: Submodule Transient. (line 40) -* magit-submodule-unpopulate: Submodule Transient. (line 45) -* magit-submodule-update: Submodule Transient. (line 36) -* magit-subtree: Subtree. (line 9) -* magit-subtree-add: Subtree. (line 24) -* magit-subtree-add-commit: Subtree. (line 28) -* magit-subtree-export: Subtree. (line 37) -* magit-subtree-import: Subtree. (line 13) -* magit-subtree-merge: Subtree. (line 31) -* magit-subtree-pull: Subtree. (line 34) -* magit-subtree-push: Subtree. (line 48) -* magit-subtree-split: Subtree. (line 52) -* magit-switch-to-repository-buffer: Common Commands. (line 6) -* magit-switch-to-repository-buffer-other-frame: Common Commands. - (line 8) -* magit-switch-to-repository-buffer-other-window: Common Commands. - (line 7) -* magit-tag: Tagging. (line 9) -* magit-tag-create: Tagging. (line 14) -* magit-tag-delete: Tagging. (line 37) -* magit-tag-prune: Tagging. (line 43) -* magit-tag-release: Tagging. (line 18) -* magit-toggle-buffer-lock: Modes and Buffers. (line 18) -* magit-toggle-git-debug: Debugging Tools. (line 29) -* magit-toggle-margin: Refreshing Logs. (line 34) -* magit-toggle-margin <1>: Log Margin. (line 60) -* magit-toggle-margin-details: Log Margin. (line 66) -* magit-toggle-verbose-refresh: Debugging Tools. (line 52) -* magit-unstage: Staging and Unstaging. - (line 42) -* magit-unstage-all: Staging and Unstaging. - (line 50) -* magit-unstage-buffer-file: Commands for Buffers Visiting Files. - (line 52) -* magit-unstage-buffer-file <1>: Commands for Buffers Visiting Files. - (line 69) -* magit-unstage-file: Staging from File-Visiting Buffers. - (line 18) -* magit-unstage-file <1>: Commands for Buffers Visiting Files. - (line 52) -* magit-unstage-file <2>: Commands for Buffers Visiting Files. - (line 69) -* magit-version: Git Executable. (line 59) -* magit-version <1>: Debugging Tools. (line 11) -* magit-visit-ref: References Buffer. (line 159) -* magit-wip-commit: Wip Modes. (line 85) -* magit-wip-log: Wip Modes. (line 47) -* magit-wip-log-current: Wip Modes. (line 55) -* magit-worktree: Worktree. (line 9) -* magit-worktree-branch: Worktree. (line 16) -* magit-worktree-checkout: Worktree. (line 13) -* magit-worktree-delete: Worktree. (line 22) -* magit-worktree-move: Worktree. (line 19) -* magit-worktree-status: Worktree. (line 26) -* scroll-down: Commands Available in Diffs. - (line 56) -* scroll-up: Commands Available in Diffs. - (line 53) -* with-editor-cancel: Editing Commit Messages. - (line 22) -* with-editor-cancel <1>: Editing Rebase Sequences. - (line 11) -* with-editor-debug: Debugging Tools. (line 64) -* with-editor-finish: Editing Commit Messages. - (line 18) -* with-editor-finish <1>: Editing Rebase Sequences. - (line 7) -* with-editor-usage-message: Commit Mode and Hooks. - (line 51) - - -File: magit.info, Node: Variable Index, Prev: Function and Command Index, Up: Top - -Appendix E Variable Index -************************* - - -* Menu: - -* auto-revert-buffer-list-filter: Automatic Reverting of File-Visiting Buffers. - (line 73) -* auto-revert-interval: Automatic Reverting of File-Visiting Buffers. - (line 69) -* auto-revert-mode: Automatic Reverting of File-Visiting Buffers. - (line 57) -* auto-revert-stop-on-user-input: Automatic Reverting of File-Visiting Buffers. - (line 65) -* auto-revert-use-notify: Automatic Reverting of File-Visiting Buffers. - (line 46) -* auto-revert-verbose: Automatic Reverting of File-Visiting Buffers. - (line 94) -* branch.autoSetupMerge: Branch Git Variables. - (line 71) -* branch.autoSetupRebase: Branch Git Variables. - (line 85) -* branch.NAME.description: Branch Git Variables. - (line 42) -* branch.NAME.merge: Branch Git Variables. - (line 10) -* branch.NAME.pushRemote: Branch Git Variables. - (line 29) -* branch.NAME.rebase: Branch Git Variables. - (line 20) -* branch.NAME.remote: Branch Git Variables. - (line 15) -* core.notesRef: Notes. (line 53) -* git-commit-finish-query-functions: Commit Message Conventions. - (line 18) -* git-commit-known-pseudo-headers: Commit Pseudo Headers. - (line 9) -* git-commit-major-mode: Commit Mode and Hooks. - (line 12) -* git-commit-post-finish-hook: Commit Mode and Hooks. - (line 54) -* git-commit-setup-hook: Commit Mode and Hooks. - (line 21) -* git-commit-style-convention-checks: Commit Message Conventions. - (line 38) -* git-commit-summary-max-length: Commit Message Conventions. - (line 13) -* git-rebase-auto-advance: Editing Rebase Sequences. - (line 80) -* git-rebase-confirm-cancel: Editing Rebase Sequences. - (line 86) -* git-rebase-show-instructions: Editing Rebase Sequences. - (line 83) -* global-auto-revert-mode: Automatic Reverting of File-Visiting Buffers. - (line 21) -* magit-auto-revert-immediately: Automatic Reverting of File-Visiting Buffers. - (line 30) -* magit-auto-revert-mode: Automatic Reverting of File-Visiting Buffers. - (line 17) -* magit-auto-revert-tracked-only: Automatic Reverting of File-Visiting Buffers. - (line 51) -* magit-bisect-show-graph: Bisecting. (line 57) -* magit-blame-disable-modes: Blaming. (line 165) -* magit-blame-echo-style: Blaming. (line 151) -* magit-blame-goto-chunk-hook: Blaming. (line 170) -* magit-blame-read-only: Blaming. (line 161) -* magit-blame-styles: Blaming. (line 147) -* magit-blame-time-format: Blaming. (line 157) -* magit-branch-adjust-remote-upstream-alist: Branch Commands. (line 202) -* magit-branch-direct-configure: Branch Commands. (line 19) -* magit-branch-prefer-remote-upstream: Branch Commands. (line 158) -* magit-branch-read-upstream-first: Branch Commands. (line 153) -* magit-buffer-name-format: Naming Buffers. (line 25) -* magit-bury-buffer-function: Quitting Windows. (line 16) -* magit-cherry-margin: Cherries. (line 21) -* magit-clone-always-transient: Cloning Repository. (line 12) -* magit-clone-default-directory: Cloning Repository. (line 84) -* magit-clone-name-alist: Cloning Repository. (line 94) -* magit-clone-set-remote-head: Cloning Repository. (line 66) -* magit-clone-set-remote.pushDefault: Cloning Repository. (line 75) -* magit-clone-url-format: Cloning Repository. (line 114) -* magit-commit-ask-to-stage: Initiating a Commit. (line 65) -* magit-commit-diff-inhibit-same-window: Initiating a Commit. (line 97) -* magit-commit-extend-override-date: Initiating a Commit. (line 72) -* magit-commit-reword-override-date: Initiating a Commit. (line 75) -* magit-commit-show-diff: Initiating a Commit. (line 69) -* magit-commit-squash-confirm: Initiating a Commit. (line 78) -* magit-completing-read-function: Support for Completion Frameworks. - (line 27) -* magit-define-global-key-bindings: Global Bindings. (line 6) -* magit-diff-adjust-tab-width: Diff Options. (line 17) -* magit-diff-buffer-file-locked: Commands for Buffers Visiting Files. - (line 104) -* magit-diff-extra-stat-arguments: Diff Options. (line 112) -* magit-diff-hide-trailing-cr-characters: Diff Options. (line 77) -* magit-diff-highlight-hunk-region-functions: Diff Options. (line 80) -* magit-diff-highlight-indentation: Diff Options. (line 63) -* magit-diff-highlight-trailing: Diff Options. (line 59) -* magit-diff-paint-whitespace: Diff Options. (line 38) -* magit-diff-paint-whitespace-lines: Diff Options. (line 52) -* magit-diff-refine-hunk: Diff Options. (line 6) -* magit-diff-refine-ignore-whitespace: Diff Options. (line 13) -* magit-diff-unmarked-lines-keep-foreground: Diff Options. (line 105) -* magit-diff-visit-previous-blob: Visiting Files and Blobs from a Diff. - (line 38) -* magit-direct-use-buffer-arguments: Transient Arguments and Buffer Variables. - (line 73) -* magit-display-buffer-function: Switching Buffers. (line 25) -* magit-display-buffer-noselect: Switching Buffers. (line 17) -* magit-dwim-selection: Completion and Confirmation. - (line 42) -* magit-ediff-dwim-resolve-function: Ediffing. (line 105) -* magit-ediff-dwim-show-on-hunks: Ediffing. (line 111) -* magit-ediff-quit-hook: Ediffing. (line 124) -* magit-ediff-show-stash-with-index: Ediffing. (line 118) -* magit-generate-buffer-name-function: Naming Buffers. (line 6) -* magit-git-debug: Viewing Git Output. (line 26) -* magit-git-debug <1>: Getting a Value from Git. - (line 68) -* magit-git-executable: Git Executable. (line 26) -* magit-git-global-arguments: Global Git Arguments. - (line 6) -* magit-keep-region-overlay: The Selection. (line 52) -* magit-list-refs-sortby: Additional Completion Options. - (line 6) -* magit-log-auto-more: Log Buffer. (line 69) -* magit-log-buffer-file-locked: Commands for Buffers Visiting Files. - (line 124) -* magit-log-margin: Log Margin. (line 12) -* magit-log-margin-show-committer-date: Log Margin. (line 44) -* magit-log-section-commit-count: Status Sections. (line 114) -* magit-log-select-margin: Select from Log. (line 28) -* magit-log-show-color-graph-limit: Log Buffer. (line 78) -* magit-log-show-refname-after-summary: Log Buffer. (line 74) -* magit-log-show-signatures-limit: Log Buffer. (line 84) -* magit-log-trace-definition-function: Commands Available in Diffs. - (line 17) -* magit-module-sections-hook: Status Module Sections. - (line 19) -* magit-module-sections-nested: Status Module Sections. - (line 22) -* magit-no-confirm: Action Confirmation. (line 18) -* magit-pop-revision-stack-format: Using the Revision Stack. - (line 34) -* magit-post-clone-hook: Cloning Repository. (line 133) -* magit-post-commit-hook: Initiating a Commit. (line 86) -* magit-post-display-buffer-hook: Switching Buffers. (line 85) -* magit-pre-display-buffer-hook: Switching Buffers. (line 76) -* magit-prefer-remote-upstream: Branch Git Variables. - (line 109) -* magit-prefix-use-buffer-arguments: Transient Arguments and Buffer Variables. - (line 65) -* magit-process-extreme-logging: Viewing Git Output. (line 56) -* magit-process-raise-error: Calling Git for Effect. - (line 125) -* magit-pull-or-fetch: Fetching. (line 52) -* magit-reflog-margin: Reflog. (line 20) -* magit-refresh-args: Refreshing Buffers. (line 52) -* magit-refresh-buffer-hook: Automatic Refreshing of Magit Buffers. - (line 41) -* magit-refresh-function: Refreshing Buffers. (line 47) -* magit-refresh-status-buffer: Automatic Refreshing of Magit Buffers. - (line 46) -* magit-refs-filter-alist: References Buffer. (line 137) -* magit-refs-focus-column-width: References Buffer. (line 75) -* magit-refs-margin: References Buffer. (line 89) -* magit-refs-margin-for-tags: References Buffer. (line 112) -* magit-refs-pad-commit-counts: References Buffer. (line 45) -* magit-refs-primary-column-width: References Buffer. (line 63) -* magit-refs-sections-hook: References Sections. (line 13) -* magit-refs-show-commit-count: References Buffer. (line 36) -* magit-refs-show-remote-prefix: References Buffer. (line 57) -* magit-remote-add-set-remote.pushDefault: Remote Commands. (line 83) -* magit-remote-direct-configure: Remote Commands. (line 20) -* magit-remote-git-executable: Git Executable. (line 32) -* magit-repolist-columns: Repository List. (line 12) -* magit-repository-directories: Status Buffer. (line 57) -* magit-revision-filter-files-on-follow: Revision Buffer. (line 55) -* magit-revision-insert-related-refs: Revision Buffer. (line 6) -* magit-revision-show-gravatars: Revision Buffer. (line 15) -* magit-revision-use-hash-sections: Revision Buffer. (line 31) -* magit-root-section: Matching Sections. (line 81) -* magit-save-repository-buffers: Automatic Saving of File-Visiting Buffers. - (line 13) -* magit-section-cache-visibility: Section Visibility. (line 95) -* magit-section-initial-visibility-alist: Section Visibility. (line 79) -* magit-section-movement-hook: Section Movement. (line 41) -* magit-section-set-visibility-hook: Section Visibility. (line 105) -* magit-section-show-child-count: Section Options. (line 9) -* magit-section-visibility-indicator: Section Visibility. (line 122) -* magit-shell-command-verbose-prompt: Running Git Manually. - (line 43) -* magit-stashes-margin: Stashing. (line 123) -* magit-status-headers-hook: Status Header Sections. - (line 17) -* magit-status-margin: Status Options. (line 6) -* magit-status-sections-hook: Status Sections. (line 10) -* magit-submodule-list-columns: Listing Submodules. (line 20) -* magit-this-process: Calling Git for Effect. - (line 121) -* magit-uniquify-buffer-names: Naming Buffers. (line 65) -* magit-unstage-committed: Staging and Unstaging. - (line 52) -* magit-update-other-window-delay: Section Movement. (line 97) -* magit-visit-ref-behavior: References Buffer. (line 168) -* magit-wip-after-apply-mode: Legacy Wip Modes. (line 18) -* magit-wip-after-apply-mode-lighter: Legacy Wip Modes. (line 54) -* magit-wip-after-save-local-mode-lighter: Legacy Wip Modes. (line 51) -* magit-wip-after-save-mode: Legacy Wip Modes. (line 13) -* magit-wip-before-change-mode: Legacy Wip Modes. (line 31) -* magit-wip-before-change-mode-lighter: Legacy Wip Modes. (line 57) -* magit-wip-initial-backup-mode: Legacy Wip Modes. (line 35) -* magit-wip-initial-backup-mode-lighter: Legacy Wip Modes. (line 60) -* magit-wip-merge-branch: Wip Graph. (line 6) -* magit-wip-mode: Wip Modes. (line 30) -* magit-wip-mode-lighter: Wip Modes. (line 98) -* magit-wip-namespace: Wip Modes. (line 91) -* notes.displayRef: Notes. (line 57) -* pull.rebase: Branch Git Variables. - (line 50) -* remote.NAME.fetch: Remote Git Variables. - (line 14) -* remote.NAME.push: Remote Git Variables. - (line 23) -* remote.NAME.pushurl: Remote Git Variables. - (line 18) -* remote.NAME.tagOpts: Remote Git Variables. - (line 27) -* remote.NAME.url: Remote Git Variables. - (line 10) -* remote.pushDefault: Branch Git Variables. - (line 62) - - - -Tag Table: -Node: Top774 -Node: Introduction6566 -Node: Installation11282 -Node: Installing from Melpa11612 -Node: Installing from the Git Repository12687 -Node: Post-Installation Tasks15501 -Node: Getting Started16786 -Node: Interface Concepts22597 -Node: Modes and Buffers22976 -Node: Switching Buffers24687 -Node: Naming Buffers29426 -Node: Quitting Windows32501 -Node: Automatic Refreshing of Magit Buffers34436 -Node: Automatic Saving of File-Visiting Buffers37317 -Node: Automatic Reverting of File-Visiting Buffers38501 -Node: Risk of Reverting Automatically43486 -Node: Sections45868 -Node: Section Movement46794 -Node: Section Visibility51668 -Node: Section Hooks58355 -Node: Section Types and Values60761 -Node: Section Options62176 -Node: Transient Commands62647 -Node: Transient Arguments and Buffer Variables64123 -Node: Completion Confirmation and the Selection71140 -Node: Action Confirmation71586 -Node: Completion and Confirmation80091 -Node: The Selection83276 -Node: The hunk-internal region86174 -Node: Support for Completion Frameworks87263 -Node: Additional Completion Options92148 -Node: Mouse Support92746 -Node: Running Git93322 -Node: Viewing Git Output93567 -Node: Git Process Status96271 -Node: Running Git Manually97236 -Node: Git Executable99926 -Node: Global Git Arguments102934 -Node: Inspecting103739 -Node: Status Buffer104896 -Node: Status Sections109907 -Node: Status Header Sections115434 -Node: Status Module Sections118053 -Node: Status Options120550 -Node: Repository List121913 -Node: Logging126691 -Node: Refreshing Logs129533 -Node: Log Buffer130954 -Node: Log Margin135777 -Node: Select from Log138930 -Node: Reflog141140 -Node: Cherries142777 -Node: Diffing144615 -Node: Refreshing Diffs147649 -Node: Commands Available in Diffs151338 -Node: Diff Options153851 -Node: Revision Buffer159314 -Node: Ediffing162634 -Node: References Buffer168684 -Node: References Sections179278 -Node: Bisecting180135 -Node: Visiting Files and Blobs182446 -Node: General-Purpose Visit Commands182974 -Node: Visiting Files and Blobs from a Diff183927 -Node: Blaming187371 -Node: Manipulating194359 -Node: Creating Repository194701 -Node: Cloning Repository195238 -Node: Staging and Unstaging201679 -Node: Staging from File-Visiting Buffers205660 -Node: Applying206766 -Node: Committing208839 -Node: Initiating a Commit209422 -Node: Editing Commit Messages214612 -Node: Using the Revision Stack217385 -Node: Commit Pseudo Headers220430 -Node: Commit Mode and Hooks221725 -Node: Commit Message Conventions224583 -Node: Branching226570 -Node: The Two Remotes226796 -Node: Branch Commands229449 -Node: Branch Git Variables242299 -Node: Auxiliary Branch Commands247673 -Node: Merging248789 -Node: Resolving Conflicts252945 -Node: Rebasing258319 -Node: Editing Rebase Sequences263108 -Node: Information About In-Progress Rebase267324 -Ref: Information About In-Progress Rebase-Footnote-1276437 -Node: Cherry Picking277033 -Node: Reverting281367 -Node: Resetting282786 -Node: Stashing284612 -Node: Transferring290993 -Node: Remotes291215 -Node: Remote Commands291367 -Node: Remote Git Variables295406 -Node: Fetching296677 -Node: Pulling299160 -Node: Pushing300186 -Node: Plain Patches304477 -Node: Maildir Patches305948 -Node: Miscellaneous307427 -Node: Tagging307773 -Node: Notes309666 -Node: Submodules312001 -Node: Listing Submodules312219 -Node: Submodule Transient314367 -Node: Subtree316812 -Node: Worktree318743 -Node: Sparse checkouts319819 -Node: Bundle322595 -Node: Common Commands322970 -Node: Wip Modes325598 -Node: Wip Graph330489 -Node: Legacy Wip Modes332802 -Node: Commands for Buffers Visiting Files335689 -Node: Minor Mode for Buffers Visiting Blobs343916 -Node: Customizing344714 -Node: Per-Repository Configuration346310 -Node: Essential Settings348564 -Node: Safety348910 -Node: Performance350671 -Ref: Log Performance353634 -Ref: Diff Performance354939 -Ref: Refs Buffer Performance356280 -Ref: Committing Performance356855 -Node: Microsoft Windows Performance357837 -Node: MacOS Performance359028 -Ref: MacOS Performance-Footnote-1360051 -Node: Global Bindings360133 -Node: Plumbing362361 -Node: Calling Git363190 -Node: Getting a Value from Git364715 -Node: Calling Git for Effect368443 -Node: Section Plumbing374337 -Node: Creating Sections374565 -Node: Section Selection378461 -Node: Matching Sections380257 -Node: Refreshing Buffers386178 -Node: Conventions389322 -Node: Theming Faces389514 -Node: FAQ397619 -Node: FAQ - How to ...?398057 -Node: How to pronounce Magit?398414 -Node: How to show git's output?399217 -Node: How to install the gitman info manual?400003 -Node: How to show diffs for gpg-encrypted files?400973 -Node: How does branching and pushing work?401569 -Node: Should I disable VC?401902 -Node: FAQ - Issues and Errors402505 -Node: Magit is slow403450 -Node: I changed several thousand files at once and now Magit is unusable403743 -Node: I am having problems committing404469 -Node: I am using MS Windows and cannot push with Magit404950 -Node: I am using macOS and SOMETHING works in shell but not in Magit405568 -Node: Expanding a file to show the diff causes it to disappear406402 -Node: Point is wrong in the COMMIT_EDITMSG buffer406990 -Node: The mode-line information isn't always up-to-date408035 -Node: A branch and tag sharing the same name breaks SOMETHING409098 -Node: My Git hooks work on the command-line but not inside Magit409985 -Node: git-commit-mode isn't used when committing from the command-line410831 -Node: Point ends up inside invisible text when jumping to a file-visiting buffer413102 -Node: I am no longer able to save popup defaults413951 -Node: Debugging Tools414932 -Node: Keystroke Index418106 -Node: Function and Command Index459720 -Node: Variable Index517921 - -End Tag Table - - -Local Variables: -coding: utf-8 -End: diff --git a/emacs/elpa/magit-20240522.204/AUTHORS.md b/emacs/elpa/magit-20240605.1500/AUTHORS.md diff --git a/emacs/elpa/magit-20240522.204/LICENSE b/emacs/elpa/magit-20240605.1500/LICENSE diff --git a/emacs/elpa/magit-20240522.204/dir b/emacs/elpa/magit-20240605.1500/dir diff --git a/emacs/elpa/magit-20240522.204/git-rebase.el b/emacs/elpa/magit-20240605.1500/git-rebase.el diff --git a/emacs/elpa/magit-20240522.204/git-rebase.elc b/emacs/elpa/magit-20240605.1500/git-rebase.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-apply.el b/emacs/elpa/magit-20240605.1500/magit-apply.el diff --git a/emacs/elpa/magit-20240522.204/magit-apply.elc b/emacs/elpa/magit-20240605.1500/magit-apply.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-autoloads.el b/emacs/elpa/magit-20240605.1500/magit-autoloads.el diff --git a/emacs/elpa/magit-20240522.204/magit-autorevert.el b/emacs/elpa/magit-20240605.1500/magit-autorevert.el diff --git a/emacs/elpa/magit-20240522.204/magit-autorevert.elc b/emacs/elpa/magit-20240605.1500/magit-autorevert.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-base.el b/emacs/elpa/magit-20240605.1500/magit-base.el diff --git a/emacs/elpa/magit-20240522.204/magit-base.elc b/emacs/elpa/magit-20240605.1500/magit-base.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-bisect.el b/emacs/elpa/magit-20240605.1500/magit-bisect.el diff --git a/emacs/elpa/magit-20240522.204/magit-bisect.elc b/emacs/elpa/magit-20240605.1500/magit-bisect.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-blame.el b/emacs/elpa/magit-20240605.1500/magit-blame.el diff --git a/emacs/elpa/magit-20240522.204/magit-blame.elc b/emacs/elpa/magit-20240605.1500/magit-blame.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-bookmark.el b/emacs/elpa/magit-20240605.1500/magit-bookmark.el diff --git a/emacs/elpa/magit-20240522.204/magit-bookmark.elc b/emacs/elpa/magit-20240605.1500/magit-bookmark.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-branch.el b/emacs/elpa/magit-20240605.1500/magit-branch.el diff --git a/emacs/elpa/magit-20240522.204/magit-branch.elc b/emacs/elpa/magit-20240605.1500/magit-branch.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-bundle.el b/emacs/elpa/magit-20240605.1500/magit-bundle.el diff --git a/emacs/elpa/magit-20240522.204/magit-bundle.elc b/emacs/elpa/magit-20240605.1500/magit-bundle.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-clone.el b/emacs/elpa/magit-20240605.1500/magit-clone.el diff --git a/emacs/elpa/magit-20240522.204/magit-clone.elc b/emacs/elpa/magit-20240605.1500/magit-clone.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-commit.el b/emacs/elpa/magit-20240605.1500/magit-commit.el diff --git a/emacs/elpa/magit-20240522.204/magit-commit.elc b/emacs/elpa/magit-20240605.1500/magit-commit.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-core.el b/emacs/elpa/magit-20240605.1500/magit-core.el diff --git a/emacs/elpa/magit-20240522.204/magit-core.elc b/emacs/elpa/magit-20240605.1500/magit-core.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-diff.el b/emacs/elpa/magit-20240605.1500/magit-diff.el diff --git a/emacs/elpa/magit-20240605.1500/magit-diff.elc b/emacs/elpa/magit-20240605.1500/magit-diff.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-ediff.el b/emacs/elpa/magit-20240605.1500/magit-ediff.el diff --git a/emacs/elpa/magit-20240522.204/magit-ediff.elc b/emacs/elpa/magit-20240605.1500/magit-ediff.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-extras.el b/emacs/elpa/magit-20240605.1500/magit-extras.el diff --git a/emacs/elpa/magit-20240522.204/magit-extras.elc b/emacs/elpa/magit-20240605.1500/magit-extras.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-fetch.el b/emacs/elpa/magit-20240605.1500/magit-fetch.el diff --git a/emacs/elpa/magit-20240522.204/magit-fetch.elc b/emacs/elpa/magit-20240605.1500/magit-fetch.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-files.el b/emacs/elpa/magit-20240605.1500/magit-files.el diff --git a/emacs/elpa/magit-20240522.204/magit-files.elc b/emacs/elpa/magit-20240605.1500/magit-files.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-git.el b/emacs/elpa/magit-20240605.1500/magit-git.el diff --git a/emacs/elpa/magit-20240522.204/magit-git.elc b/emacs/elpa/magit-20240605.1500/magit-git.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-gitignore.el b/emacs/elpa/magit-20240605.1500/magit-gitignore.el diff --git a/emacs/elpa/magit-20240522.204/magit-gitignore.elc b/emacs/elpa/magit-20240605.1500/magit-gitignore.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-log.el b/emacs/elpa/magit-20240605.1500/magit-log.el diff --git a/emacs/elpa/magit-20240522.204/magit-log.elc b/emacs/elpa/magit-20240605.1500/magit-log.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-margin.el b/emacs/elpa/magit-20240605.1500/magit-margin.el diff --git a/emacs/elpa/magit-20240522.204/magit-margin.elc b/emacs/elpa/magit-20240605.1500/magit-margin.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-merge.el b/emacs/elpa/magit-20240605.1500/magit-merge.el diff --git a/emacs/elpa/magit-20240522.204/magit-merge.elc b/emacs/elpa/magit-20240605.1500/magit-merge.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-mode.el b/emacs/elpa/magit-20240605.1500/magit-mode.el diff --git a/emacs/elpa/magit-20240522.204/magit-mode.elc b/emacs/elpa/magit-20240605.1500/magit-mode.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-notes.el b/emacs/elpa/magit-20240605.1500/magit-notes.el diff --git a/emacs/elpa/magit-20240522.204/magit-notes.elc b/emacs/elpa/magit-20240605.1500/magit-notes.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-patch.el b/emacs/elpa/magit-20240605.1500/magit-patch.el diff --git a/emacs/elpa/magit-20240522.204/magit-patch.elc b/emacs/elpa/magit-20240605.1500/magit-patch.elc Binary files differ. diff --git a/emacs/elpa/magit-20240605.1500/magit-pkg.el b/emacs/elpa/magit-20240605.1500/magit-pkg.el @@ -0,0 +1,20 @@ +(define-package "magit" "20240605.1500" "A Git porcelain inside Emacs." + '((emacs "26.1") + (compat "29.1.4.5") + (dash "20240405") + (git-commit "20240429") + (magit-section "20240429") + (seq "2.24") + (transient "20240421") + (with-editor "20240415")) + :commit "8b2d4b03ecf9635c165d1c0f90cd6f2eb415cafa" :authors + '(("Marius Vollmer" . "marius.vollmer@gmail.com") + ("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev")) + :maintainer + '("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") + :keywords + '("git" "tools" "vc") + :url "https://github.com/magit/magit") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs/elpa/magit-20240522.204/magit-process.el b/emacs/elpa/magit-20240605.1500/magit-process.el diff --git a/emacs/elpa/magit-20240522.204/magit-process.elc b/emacs/elpa/magit-20240605.1500/magit-process.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-pull.el b/emacs/elpa/magit-20240605.1500/magit-pull.el diff --git a/emacs/elpa/magit-20240522.204/magit-pull.elc b/emacs/elpa/magit-20240605.1500/magit-pull.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-push.el b/emacs/elpa/magit-20240605.1500/magit-push.el diff --git a/emacs/elpa/magit-20240522.204/magit-push.elc b/emacs/elpa/magit-20240605.1500/magit-push.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-reflog.el b/emacs/elpa/magit-20240605.1500/magit-reflog.el diff --git a/emacs/elpa/magit-20240522.204/magit-reflog.elc b/emacs/elpa/magit-20240605.1500/magit-reflog.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-refs.el b/emacs/elpa/magit-20240605.1500/magit-refs.el diff --git a/emacs/elpa/magit-20240522.204/magit-refs.elc b/emacs/elpa/magit-20240605.1500/magit-refs.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-remote.el b/emacs/elpa/magit-20240605.1500/magit-remote.el diff --git a/emacs/elpa/magit-20240522.204/magit-remote.elc b/emacs/elpa/magit-20240605.1500/magit-remote.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-repos.el b/emacs/elpa/magit-20240605.1500/magit-repos.el diff --git a/emacs/elpa/magit-20240522.204/magit-repos.elc b/emacs/elpa/magit-20240605.1500/magit-repos.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-reset.el b/emacs/elpa/magit-20240605.1500/magit-reset.el diff --git a/emacs/elpa/magit-20240522.204/magit-reset.elc b/emacs/elpa/magit-20240605.1500/magit-reset.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-sequence.el b/emacs/elpa/magit-20240605.1500/magit-sequence.el diff --git a/emacs/elpa/magit-20240522.204/magit-sequence.elc b/emacs/elpa/magit-20240605.1500/magit-sequence.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-sparse-checkout.el b/emacs/elpa/magit-20240605.1500/magit-sparse-checkout.el diff --git a/emacs/elpa/magit-20240522.204/magit-sparse-checkout.elc b/emacs/elpa/magit-20240605.1500/magit-sparse-checkout.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-stash.el b/emacs/elpa/magit-20240605.1500/magit-stash.el diff --git a/emacs/elpa/magit-20240522.204/magit-stash.elc b/emacs/elpa/magit-20240605.1500/magit-stash.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-status.el b/emacs/elpa/magit-20240605.1500/magit-status.el diff --git a/emacs/elpa/magit-20240522.204/magit-status.elc b/emacs/elpa/magit-20240605.1500/magit-status.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-submodule.el b/emacs/elpa/magit-20240605.1500/magit-submodule.el diff --git a/emacs/elpa/magit-20240522.204/magit-submodule.elc b/emacs/elpa/magit-20240605.1500/magit-submodule.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-subtree.el b/emacs/elpa/magit-20240605.1500/magit-subtree.el diff --git a/emacs/elpa/magit-20240522.204/magit-subtree.elc b/emacs/elpa/magit-20240605.1500/magit-subtree.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-tag.el b/emacs/elpa/magit-20240605.1500/magit-tag.el diff --git a/emacs/elpa/magit-20240522.204/magit-tag.elc b/emacs/elpa/magit-20240605.1500/magit-tag.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-transient.el b/emacs/elpa/magit-20240605.1500/magit-transient.el diff --git a/emacs/elpa/magit-20240605.1500/magit-transient.elc b/emacs/elpa/magit-20240605.1500/magit-transient.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-wip.el b/emacs/elpa/magit-20240605.1500/magit-wip.el diff --git a/emacs/elpa/magit-20240522.204/magit-wip.elc b/emacs/elpa/magit-20240605.1500/magit-wip.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit-worktree.el b/emacs/elpa/magit-20240605.1500/magit-worktree.el diff --git a/emacs/elpa/magit-20240522.204/magit-worktree.elc b/emacs/elpa/magit-20240605.1500/magit-worktree.elc Binary files differ. diff --git a/emacs/elpa/magit-20240522.204/magit.el b/emacs/elpa/magit-20240605.1500/magit.el diff --git a/emacs/elpa/magit-20240522.204/magit.elc b/emacs/elpa/magit-20240605.1500/magit.elc Binary files differ. diff --git a/emacs/elpa/magit-20240605.1500/magit.info b/emacs/elpa/magit-20240605.1500/magit.info @@ -0,0 +1,11592 @@ +This is magit.info, produced by makeinfo version 6.7 from magit.texi. + + Copyright (C) 2015-2024 Jonas Bernoulli + <emacs.magit@jonas.bernoulli.dev> + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +INFO-DIR-SECTION Emacs +START-INFO-DIR-ENTRY +* Magit: (magit). Using Git from Emacs with Magit. +END-INFO-DIR-ENTRY + + +File: magit.info, Node: Top, Next: Introduction, Up: (dir) + +Magit User Manual +***************** + +Magit is an interface to the version control system Git, implemented as +an Emacs package. Magit aspires to be a complete Git porcelain. While +we cannot (yet) claim that Magit wraps and improves upon each and every +Git command, it is complete enough to allow even experienced Git users +to perform almost all of their daily version control tasks directly from +within Emacs. While many fine Git clients exist, only Magit and Git +itself deserve to be called porcelains. + +This manual is for Magit version 3.3.0.50-git. + + Copyright (C) 2015-2024 Jonas Bernoulli + <emacs.magit@jonas.bernoulli.dev> + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +* Menu: + +* Introduction:: +* Installation:: +* Getting Started:: +* Interface Concepts:: +* Inspecting:: +* Manipulating:: +* Transferring:: +* Miscellaneous:: +* Customizing:: +* Plumbing:: +* FAQ:: +* Debugging Tools:: +* Keystroke Index:: +* Function and Command Index:: +* Variable Index:: + +— The Detailed Node Listing — + +Installation + +* Installing from Melpa:: +* Installing from the Git Repository:: +* Post-Installation Tasks:: + +Interface Concepts + +* Modes and Buffers:: +* Sections:: +* Transient Commands:: +* Transient Arguments and Buffer Variables:: +* Completion, Confirmation and the Selection: Completion Confirmation and the Selection. +* Mouse Support:: +* Running Git:: + +Modes and Buffers + +* Switching Buffers:: +* Naming Buffers:: +* Quitting Windows:: +* Automatic Refreshing of Magit Buffers:: +* Automatic Saving of File-Visiting Buffers:: +* Automatic Reverting of File-Visiting Buffers:: + + +Sections + +* Section Movement:: +* Section Visibility:: +* Section Hooks:: +* Section Types and Values:: +* Section Options:: + + +Completion, Confirmation and the Selection + +* Action Confirmation:: +* Completion and Confirmation:: +* The Selection:: +* The hunk-internal region:: +* Support for Completion Frameworks:: +* Additional Completion Options:: + + +Running Git + +* Viewing Git Output:: +* Git Process Status:: +* Running Git Manually:: +* Git Executable:: +* Global Git Arguments:: + + +Inspecting + +* Status Buffer:: +* Repository List:: +* Logging:: +* Diffing:: +* Ediffing:: +* References Buffer:: +* Bisecting:: +* Visiting Files and Blobs:: +* Blaming:: + +Status Buffer + +* Status Sections:: +* Status Header Sections:: +* Status Module Sections:: +* Status Options:: + + +Logging + +* Refreshing Logs:: +* Log Buffer:: +* Log Margin:: +* Select from Log:: +* Reflog:: +* Cherries:: + + +Diffing + +* Refreshing Diffs:: +* Commands Available in Diffs:: +* Diff Options:: +* Revision Buffer:: + + +References Buffer + +* References Sections:: + + +Visiting Files and Blobs + +* General-Purpose Visit Commands:: +* Visiting Files and Blobs from a Diff:: + + +Manipulating + +* Creating Repository:: +* Cloning Repository:: +* Staging and Unstaging:: +* Applying:: +* Committing:: +* Branching:: +* Merging:: +* Resolving Conflicts:: +* Rebasing:: +* Cherry Picking:: +* Resetting:: +* Stashing:: + +Staging and Unstaging + +* Staging from File-Visiting Buffers:: + + +Committing + +* Initiating a Commit:: +* Editing Commit Messages:: + + +Branching + +* The Two Remotes:: +* Branch Commands:: +* Branch Git Variables:: +* Auxiliary Branch Commands:: + + +Rebasing + +* Editing Rebase Sequences:: +* Information About In-Progress Rebase:: + + +Cherry Picking + +* Reverting:: + + +Transferring + +* Remotes:: +* Fetching:: +* Pulling:: +* Pushing:: +* Plain Patches:: +* Maildir Patches:: + +Remotes + +* Remote Commands:: +* Remote Git Variables:: + + +Miscellaneous + +* Tagging:: +* Notes:: +* Submodules:: +* Subtree:: +* Worktree:: +* Sparse checkouts:: +* Bundle:: +* Common Commands:: +* Wip Modes:: +* Commands for Buffers Visiting Files:: +* Minor Mode for Buffers Visiting Blobs:: + +Submodules + +* Listing Submodules:: +* Submodule Transient:: + + +Wip Modes + +* Wip Graph:: +* Legacy Wip Modes:: + + +Customizing + +* Per-Repository Configuration:: +* Essential Settings:: + +Essential Settings + +* Safety:: +* Performance:: +* Global Bindings:: + + +Plumbing + +* Calling Git:: +* Section Plumbing:: +* Refreshing Buffers:: +* Conventions:: + +Calling Git + +* Getting a Value from Git:: +* Calling Git for Effect:: + + +Section Plumbing + +* Creating Sections:: +* Section Selection:: +* Matching Sections:: + + +Conventions + +* Theming Faces:: + + +FAQ + +* FAQ - How to ...?:: +* FAQ - Issues and Errors:: + +FAQ - How to ...? + +* How to pronounce Magit?:: +* How to show git's output?:: +* How to install the gitman info manual?:: +* How to show diffs for gpg-encrypted files?:: +* How does branching and pushing work?:: +* Should I disable VC?:: + + +FAQ - Issues and Errors + +* Magit is slow:: +* I changed several thousand files at once and now Magit is unusable:: +* I am having problems committing:: +* I am using MS Windows and cannot push with Magit:: +* I am using macOS and SOMETHING works in shell, but not in Magit: I am using macOS and SOMETHING works in shell but not in Magit. +* Expanding a file to show the diff causes it to disappear:: +* Point is wrong in the COMMIT_EDITMSG buffer:: +* The mode-line information isn't always up-to-date:: +* A branch and tag sharing the same name breaks SOMETHING:: +* My Git hooks work on the command-line but not inside Magit:: +* git-commit-mode isn't used when committing from the command-line:: +* Point ends up inside invisible text when jumping to a file-visiting buffer:: +* I am no longer able to save popup defaults:: + + + + +File: magit.info, Node: Introduction, Next: Installation, Prev: Top, Up: Top + +1 Introduction +************** + +Magit is an interface to the version control system Git, implemented as +an Emacs package. Magit aspires to be a complete Git porcelain. While +we cannot (yet) claim that Magit wraps and improves upon each and every +Git command, it is complete enough to allow even experienced Git users +to perform almost all of their daily version control tasks directly from +within Emacs. While many fine Git clients exist, only Magit and Git +itself deserve to be called porcelains. + + Staging and otherwise applying changes is one of the most important +features in a Git porcelain and here Magit outshines anything else, +including Git itself. Git’s own staging interface (‘git add --patch’) +is so cumbersome that many users only use it in exceptional cases. In +Magit staging a hunk or even just part of a hunk is as trivial as +staging all changes made to a file. + + The most visible part of Magit’s interface is the status buffer, +which displays information about the current repository. Its content is +created by running several Git commands and making their output +actionable. Among other things, it displays information about the +current branch, lists unpulled and unpushed changes and contains +sections displaying the staged and unstaged changes. That might sound +noisy, but, since sections are collapsible, it’s not. + + To stage or unstage a change one places the cursor on the change and +then types ‘s’ or ‘u’. The change can be a file or a hunk, or when the +region is active (i.e., when there is a selection) several files or +hunks, or even just part of a hunk. The change or changes that these +commands - and many others - would act on are highlighted. + + Magit also implements several other "apply variants" in addition to +staging and unstaging. One can discard or reverse a change, or apply it +to the working tree. Git’s own porcelain only supports this for staging +and unstaging and you would have to do something like ‘git diff ... | +??? | git apply ...’ to discard, revert, or apply a single hunk on the +command line. In fact that’s exactly what Magit does internally (which +is what lead to the term "apply variants"). + + Magit isn’t just for Git experts, but it does assume some prior +experience with Git as well as Emacs. That being said, many users have +reported that using Magit was what finally taught them what Git is +capable of and how to use it to its fullest. Other users wished they +had switched to Emacs sooner so that they would have gotten their hands +on Magit earlier. + + While one has to know the basic features of Emacs to be able to make +full use of Magit, acquiring just enough Emacs skills doesn’t take long +and is worth it, even for users who prefer other editors. Vim users are +advised to give Evil (https://github.com/emacs-evil/evil), the +"Extensible VI Layer for Emacs", and Spacemacs +(https://github.com/syl20bnr/spacemacs), an "Emacs starter-kit focused +on Evil" a try. + + Magit provides a consistent and efficient Git porcelain. After a +short learning period, you will be able to perform most of your daily +version control tasks faster than you would on the command line. You +will likely also start using features that seemed too daunting in the +past. + + Magit fully embraces Git. It exposes many advanced features using a +simple but flexible interface instead of only wrapping the trivial ones +like many GUI clients do. Of course Magit supports logging, cloning, +pushing, and other commands that usually don’t fail in spectacular ways; +but it also supports tasks that often cannot be completed in a single +step. Magit fully supports tasks such as merging, rebasing, +cherry-picking, reverting, and blaming by not only providing a command +to initiate these tasks but also by displaying context sensitive +information along the way and providing commands that are useful for +resolving conflicts and resuming the sequence after doing so. + + Magit wraps and in many cases improves upon at least the following +Git porcelain commands: ‘add’, ‘am’, ‘bisect’, ‘blame’, ‘branch’, +‘checkout’, ‘cherry’, ‘cherry-pick’, ‘clean’, ‘clone’, ‘commit’, +‘config’, ‘describe’, ‘diff’, ‘fetch’, ‘format-patch’, ‘init’, ‘log’, +‘merge’, ‘merge-tree’, ‘mv’, ‘notes’, ‘pull’, ‘rebase’, ‘reflog’, +‘remote’, ‘request-pull’, ‘reset’, ‘revert’, ‘rm’, ‘show’, ‘stash’, +‘submodule’, ‘subtree’, ‘tag’, and ‘worktree.’ Many more Magit porcelain +commands are implemented on top of Git plumbing commands. + + +File: magit.info, Node: Installation, Next: Getting Started, Prev: Introduction, Up: Top + +2 Installation +************** + +Magit can be installed using Emacs’ package manager or manually from its +development repository. + +* Menu: + +* Installing from Melpa:: +* Installing from the Git Repository:: +* Post-Installation Tasks:: + + +File: magit.info, Node: Installing from Melpa, Next: Installing from the Git Repository, Up: Installation + +2.1 Installing from Melpa +========================= + +Magit is available from Melpa and Melpa-Stable. If you haven’t used +Emacs’ package manager before, then it is high time you familiarize +yourself with it by reading the documentation in the Emacs manual, see +*note (emacs)Packages::. Then add one of the archives to +‘package-archives’: + + • To use Melpa: + + (require 'package) + (add-to-list 'package-archives + '("melpa" . "https://melpa.org/packages/") t) + + • To use Melpa-Stable: + + (require 'package) + (add-to-list 'package-archives + '("melpa-stable" . "https://stable.melpa.org/packages/") t) + + Once you have added your preferred archive, you need to update the +local package list using: + + M-x package-refresh-contents RET + + Once you have done that, you can install Magit and its dependencies +using: + + M-x package-install RET magit RET + + Now see *note Post-Installation Tasks::. + + +File: magit.info, Node: Installing from the Git Repository, Next: Post-Installation Tasks, Prev: Installing from Melpa, Up: Installation + +2.2 Installing from the Git Repository +====================================== + +Magit depends on the ‘compat’, ‘dash’, ‘transient’ and ‘with-editor’ +libraries which are available from Melpa and Melpa-Stable. Install them +using ‘M-x package-install RET <package> RET’. Of course you may also +install them manually from their repository. + + Then clone the Magit repository: + + $ git clone https://github.com/magit/magit.git ~/.emacs.d/site-lisp/magit + $ cd ~/.emacs.d/site-lisp/magit + + Then compile the libraries and generate the info manuals: + + $ make + + If you haven’t installed ‘compat’, ‘dash’, ‘transient’ and +‘with-editor’ from Melpa or at ‘/path/to/magit/../<package>’, then you +have to tell ‘make’ where to find them. To do so create the file +‘/path/to/magit/config.mk’ with the following content before running +‘make’: + + LOAD_PATH = -L ~/.emacs.d/site-lisp/magit/lisp + LOAD_PATH += -L ~/.emacs.d/site-lisp/dash + LOAD_PATH += -L ~/.emacs.d/site-lisp/transient/lisp + LOAD_PATH += -L ~/.emacs.d/site-lisp/with-editor/lisp + LOAD_PATH += -L ~/.emacs.d/site-lisp/compat + + Finally add this to your init file: + + (add-to-list 'load-path "~/.emacs.d/site-lisp/magit/lisp") + (require 'magit) + + (with-eval-after-load 'info + (info-initialize) + (add-to-list 'Info-directory-list + "~/.emacs.d/site-lisp/magit/Documentation/")) + + Of course if you installed the dependencies manually as well, then +you have to tell Emacs about them too, by prefixing the above with: + + (add-to-list 'load-path "~/.emacs.d/site-lisp/dash") + (add-to-list 'load-path "~/.emacs.d/site-lisp/transient/lisp") + (add-to-list 'load-path "~/.emacs.d/site-lisp/with-editor") + + Note that you have to add the ‘lisp’ subdirectory to the ‘load-path’, +not the top-level of the repository, and that elements of ‘load-path’ +should not end with a slash, while those of ‘Info-directory-list’ +should. + + Instead of requiring the feature ‘magit’, you could load just the +autoload definitions, by loading the file ‘magit-autoloads.el’. + + (load "/path/to/magit/lisp/magit-autoloads") + + Instead of running Magit directly from the repository by adding that +to the ‘load-path’, you might want to instead install it in some other +directory using ‘sudo make install’ and setting ‘load-path’ accordingly. + + To update Magit use: + + $ git pull + $ make + + At times it might be necessary to run ‘make clean all’ instead. + + To view all available targets use ‘make help’. + + Now see *note Post-Installation Tasks::. + + +File: magit.info, Node: Post-Installation Tasks, Prev: Installing from the Git Repository, Up: Installation + +2.3 Post-Installation Tasks +=========================== + +After installing Magit you should verify that you are indeed using the +Magit, Git, and Emacs releases you think you are using. It’s best to +restart Emacs before doing so, to make sure you are not using an +outdated value for ‘load-path’. + + M-x magit-version RET + + should display something like + + Magit 2.8.0, Git 2.10.2, Emacs 25.1.1, gnu/linux + + Then you might also want to read about options that many users likely +want to customize. See *note Essential Settings::. + + To be able to follow cross references to Git manpages found in this +manual, you might also have to manually install the ‘gitman’ info +manual, or advice ‘Info-follow-nearest-node’ to instead open the actual +manpage. See *note How to install the gitman info manual?::. + + If you are completely new to Magit then see *note Getting Started::. + + If you run into problems, then please see the *note FAQ::. Also see +the *note Debugging Tools::. + + And last but not least please consider making a donation, to ensure +that I can keep working on Magit. See <https://magit.vc/donations>. +for various donation options. + + +File: magit.info, Node: Getting Started, Next: Interface Concepts, Prev: Installation, Up: Top + +3 Getting Started +***************** + +This short tutorial describes the most essential features that many +Magitians use on a daily basis. It only scratches the surface but +should be enough to get you started. + + IMPORTANT: It is safest if you clone some repository just for this +tutorial. Alternatively you can use an existing local repository, but +if you do that, then you should commit all uncommitted changes before +proceeding. + + Type ‘C-x g’ to display information about the current Git repository +in a dedicated buffer, called the status buffer. + + Most Magit commands are commonly invoked from the status buffer. It +can be considered the primary interface for interacting with Git using +Magit. Many other Magit buffers may exist at a given time, but they are +often created from this buffer. + + Depending on what state your repository is in, this buffer may +contain sections titled "Staged changes", "Unstaged changes", "Unmerged +into origin/master", "Unpushed to origin/master", and many others. + + Since we are starting from a safe state, which you can easily return +to (by doing a ‘git reset --hard PRE-MAGIT-STATE’), there currently are +no staged or unstaged changes. Edit some files and save the changes. +Then go back to the status buffer, while at the same time refreshing it, +by typing ‘C-x g’. (When the status buffer, or any Magit buffer for +that matter, is the current buffer, then you can also use just ‘g’ to +refresh it). + + Move between sections using ‘p’ and ‘n’. Note that the bodies of +some sections are hidden. Type ‘TAB’ to expand or collapse the section +at point. You can also use ‘C-tab’ to cycle the visibility of the +current section and its children. Move to a file section inside the +section named "Unstaged changes" and type ‘s’ to stage the changes you +have made to that file. That file now appears under "Staged changes". + + Magit can stage and unstage individual hunks, not just complete +files. Move to the file you have just staged, expand it using ‘TAB’, +move to one of the hunks using ‘n’, and unstage just that by typing ‘u’. +Note how the staging (‘s’) and unstaging (‘u’) commands operate on the +change at point. Many other commands behave the same way. + + You can also un-/stage just part of a hunk. Inside the body of a +hunk section (move there using ‘C-n’), set the mark using ‘C-SPC’ and +move down until some added and/or removed lines fall inside the region +but not all of them. Again type ‘s’ to stage. + + It is also possible to un-/stage multiple files at once. Move to a +file section, type ‘C-SPC’, move to the next file using ‘n’, and then +‘s’ to stage both files. Note that both the mark and point have to be +on the headings of sibling sections for this to work. If the region +looks like it does in other buffers, then it doesn’t select Magit +sections that can be acted on as a unit. + + And then of course you want to commit your changes. Type ‘c’. This +shows the available commit commands and arguments in a buffer at the +bottom of the frame. Each command and argument is prefixed with the key +that invokes/sets it. Do not worry about this for now. We want to +create a "normal" commit, which is done by typing ‘c’ again. + + Now two new buffers appear. One is for writing the commit message, +the other shows a diff with the changes that you are about to commit. +Write a message and then type ‘C-c C-c’ to actually create the commit. + + You probably don’t want to push the commit you just created because +you just committed some random changes, but if that is not the case you +could push it by typing ‘P’ to show all the available push commands and +arguments and then ‘p’ to push to a branch with the same name as the +local branch onto the remote configured as the push-remote. (If the +push-remote is not configured yet, then you would first be prompted for +the remote to push to.) + + So far we have mentioned the commit and push menu commands. These +are probably among the menus you will be using the most, but many others +exist. To show a menu that lists all other menus (as well as the +various apply commands and some other essential commands), type ‘h’. +Try a few. (Such menus are also called "transient prefix commands" or +just "transients".) + + The key bindings in that menu correspond to the bindings in Magit +buffers, including but not limited to the status buffer. So you could +type ‘h d’ to bring up the diff menu, but once you remember that "d" +stands for "diff", you would usually do so by just typing ‘d’. + + This "prefix of prefixes" is useful even once you have memorized all +the bindings, as it can provide easy access to Magit commands from +non-Magit buffers. So, by default, it is globally bound to ‘C-x M-g’. + + A similar menu featuring (for the most part) commands that act on +just the file being visited in the current buffer, is globally bound to +‘C-c M-g’. That binding can also be used in buffers, which do not visit +a file, but then only a subset of the commands is available. + + The global key bindings mentioned in the previous two paragraphs are +quite inconvenient. We recommend using ‘C-c g’ and ‘C-c f’ instead, but +cannot use those key sequences by default because they are strictly +reserved for bindings added by the user. See *note Global Bindings::, +if you want to explicitly opt-in to the recommended key bindings. + + Magit also provides context menus and other mouse commands, see *note +Mouse Support::. + + It is not necessary that you do so now, but if you stick with Magit, +then it is highly recommended that you read the next section too. + + +File: magit.info, Node: Interface Concepts, Next: Inspecting, Prev: Getting Started, Up: Top + +4 Interface Concepts +******************** + +* Menu: + +* Modes and Buffers:: +* Sections:: +* Transient Commands:: +* Transient Arguments and Buffer Variables:: +* Completion, Confirmation and the Selection: Completion Confirmation and the Selection. +* Mouse Support:: +* Running Git:: + + +File: magit.info, Node: Modes and Buffers, Next: Sections, Up: Interface Concepts + +4.1 Modes and Buffers +===================== + +Magit provides several major-modes. For each of these modes there +usually exists only one buffer per repository. Separate modes and thus +buffers exist for commits, diffs, logs, and some other things. + + Besides these special purpose buffers, there also exists an overview +buffer, called the *status buffer*. It’s usually from this buffer that +the user invokes Git commands, or creates or visits other buffers. + + In this manual we often speak about "Magit buffers". By that we mean +buffers whose major-modes derive from ‘magit-mode’. + +‘M-x magit-toggle-buffer-lock’ + This command locks the current buffer to its value or if the buffer + is already locked, then it unlocks it. + + Locking a buffer to its value prevents it from being reused to + display another value. The name of a locked buffer contains its + value, which allows telling it apart from other locked buffers and + the unlocked buffer. + + Not all Magit buffers can be locked to their values; for example, + it wouldn’t make sense to lock a status buffer. + + There can only be a single unlocked buffer using a certain + major-mode per repository. So when a buffer is being unlocked and + another unlocked buffer already exists for that mode and + repository, then the former buffer is instead deleted and the + latter is displayed in its place. + +* Menu: + +* Switching Buffers:: +* Naming Buffers:: +* Quitting Windows:: +* Automatic Refreshing of Magit Buffers:: +* Automatic Saving of File-Visiting Buffers:: +* Automatic Reverting of File-Visiting Buffers:: + + +File: magit.info, Node: Switching Buffers, Next: Naming Buffers, Up: Modes and Buffers + +4.1.1 Switching Buffers +----------------------- + + -- Function: magit-display-buffer buffer &optional display-function + This function is a wrapper around ‘display-buffer’ and is used to + display any Magit buffer. It displays BUFFER in some window and, + unlike ‘display-buffer’, also selects that window, provided + ‘magit-display-buffer-noselect’ is ‘nil’. It also runs the hooks + mentioned below. + + If optional DISPLAY-FUNCTION is non-nil, then that is used to + display the buffer. Usually that is ‘nil’ and the function + specified by ‘magit-display-buffer-function’ is used. + + -- Variable: magit-display-buffer-noselect + When this is non-nil, then ‘magit-display-buffer’ only displays the + buffer but forgoes also selecting the window. This variable should + not be set globally, it is only intended to be let-bound, by code + that automatically updates "the other window". This is used for + example when the revision buffer is updated when you move inside + the log buffer. + + -- User Option: magit-display-buffer-function + The function specified here is called by ‘magit-display-buffer’ + with one argument, a buffer, to actually display that buffer. This + function should call ‘display-buffer’ with that buffer as first and + a list of display actions as second argument. + + Magit provides several functions, listed below, that are suitable + values for this option. If you want to use different rules, then a + good way of doing that is to start with a copy of one of these + functions and then adjust it to your needs. + + Instead of using a wrapper around ‘display-buffer’, that function + itself can be used here, in which case the display actions have to + be specified by adding them to ‘display-buffer-alist’ instead. + + To learn about display actions, see *note (elisp)Choosing Window::. + + -- Function: magit-display-buffer-traditional buffer + This function is the current default value of the option + ‘magit-display-buffer-function’. Before that option and this + function were added, the behavior was hard-coded in many places all + over the code base but now all the rules are contained in this one + function (except for the "noselect" special case mentioned above). + + -- Function: magit-display-buffer-same-window-except-diff-v1 + This function displays most buffers in the currently selected + window. If a buffer’s mode derives from ‘magit-diff-mode’ or + ‘magit-process-mode’, it is displayed in another window. + + -- Function: magit-display-buffer-fullframe-status-v1 + This function fills the entire frame when displaying a status + buffer. Otherwise, it behaves like + ‘magit-display-buffer-traditional’. + + -- Function: magit-display-buffer-fullframe-status-topleft-v1 + This function fills the entire frame when displaying a status + buffer. It behaves like ‘magit-display-buffer-fullframe-status-v1’ + except that it displays buffers that derive from ‘magit-diff-mode’ + or ‘magit-process-mode’ to the top or left of the current buffer + rather than to the bottom or right. As a result, Magit buffers + tend to pop up on the same side as they would if + ‘magit-display-buffer-traditional’ were in use. + + -- Function: magit-display-buffer-fullcolumn-most-v1 + This function displays most buffers so that they fill the entire + height of the frame. However, the buffer is displayed in another + window if (1) the buffer’s mode derives from ‘magit-process-mode’, + or (2) the buffer’s mode derives from ‘magit-diff-mode’, provided + that the mode of the current buffer derives from ‘magit-log-mode’ + or ‘magit-cherry-mode’. + + -- User Option: magit-pre-display-buffer-hook + This hook is run by ‘magit-display-buffer’ before displaying the + buffer. + + -- Function: magit-save-window-configuration + This function saves the current window configuration. Later when + the buffer is buried, it may be restored by + ‘magit-restore-window-configuration’. + + -- User Option: magit-post-display-buffer-hook + This hook is run by ‘magit-display-buffer’ after displaying the + buffer. + + -- Function: magit-maybe-set-dedicated + This function remembers if a new window had to be created to + display the buffer, or whether an existing window was reused. This + information is later used by ‘magit-mode-quit-window’, to determine + whether the window should be deleted when its last Magit buffer is + buried. + + +File: magit.info, Node: Naming Buffers, Next: Quitting Windows, Prev: Switching Buffers, Up: Modes and Buffers + +4.1.2 Naming Buffers +-------------------- + + -- User Option: magit-generate-buffer-name-function + The function used to generate the names of Magit buffers. + + Such a function should take the options + ‘magit-uniquify-buffer-names’ as well as ‘magit-buffer-name-format’ + into account. If it doesn’t, then should be clearly stated in the + doc-string. And if it supports %-sequences beyond those mentioned + in the doc-string of the option ‘magit-buffer-name-format’, then + its own doc-string should describe the additions. + + -- Function: magit-generate-buffer-name-default-function mode + This function returns a buffer name suitable for a buffer whose + major-mode is MODE and which shows information about the repository + in which ‘default-directory’ is located. + + This function uses ‘magit-buffer-name-format’ and supporting all of + the %-sequences mentioned the documentation of that option. It + also respects the option ‘magit-uniquify-buffer-names’. + + -- User Option: magit-buffer-name-format + The format string used to name Magit buffers. + + At least the following %-sequences are supported: + + • ‘%m’ + + The name of the major-mode, but with the ‘-mode’ suffix + removed. + + • ‘%M’ + + Like ‘%m’ but abbreviate ‘magit-status-mode’ as ‘magit’. + + • ‘%v’ + + The value the buffer is locked to, in parentheses, or an empty + string if the buffer is not locked to a value. + + • ‘%V’ + + Like ‘%v’, but the string is prefixed with a space, unless it + is an empty string. + + • ‘%t’ + + The top-level directory of the working tree of the repository, + or if ‘magit-uniquify-buffer-names’ is non-nil an abbreviation + of that. + + • ‘%x’ + + If ‘magit-uniquify-buffer-names’ is nil "*", otherwise the + empty string. Due to limitations of the ‘uniquify’ package, + buffer names must end with the path. + + The value should always contain ‘%m’ or ‘%M’, ‘%v’ or ‘%V’, and + ‘%t’. If ‘magit-uniquify-buffer-names’ is non-nil, then the value + must end with ‘%t’ or ‘%t%x’. See issue #2841. + + -- User Option: magit-uniquify-buffer-names + This option controls whether the names of Magit buffers are + uniquified. If the names are not being uniquified, then they + contain the full path of the top-level of the working tree of the + corresponding repository. If they are being uniquified, then they + end with the basename of the top-level, or if that would conflict + with the name used for other buffers, then the names of all these + buffers are adjusted until they no longer conflict. + + This is done using the ‘uniquify’ package; customize its options to + control how buffer names are uniquified. + + +File: magit.info, Node: Quitting Windows, Next: Automatic Refreshing of Magit Buffers, Prev: Naming Buffers, Up: Modes and Buffers + +4.1.3 Quitting Windows +---------------------- + +‘q’ (‘magit-mode-bury-buffer’) + This command buries or kills the current Magit buffer. The + function specified by option ‘magit-bury-buffer-function’ is used + to bury the buffer when called without a prefix argument or to kill + it when called with a single prefix argument. + + When called with two or more prefix arguments then it always kills + all Magit buffers, associated with the current project, including + the current buffer. + + -- User Option: magit-bury-buffer-function + The function used to actually bury or kill the current buffer. + + ‘magit-mode-bury-buffer’ calls this function with one argument. If + the argument is non-nil, then the function has to kill the current + buffer. Otherwise it has to bury it alive. The default value + currently is ‘magit-mode-quit-window’. + + -- Function: magit-restore-window-configuration kill-buffer + Bury or kill the current buffer using ‘quit-window’, which is + called with KILL-BUFFER as first and the selected window as second + argument. + + Then restore the window configuration that existed right before the + current buffer was displayed in the selected frame. Unfortunately + that also means that point gets adjusted in all the buffers, which + are being displayed in the selected frame. + + -- Function: magit-mode-quit-window kill-buffer + Bury or kill the current buffer using ‘quit-window’, which is + called with KILL-BUFFER as first and the selected window as second + argument. + + Then, if the window was originally created to display a Magit + buffer and the buried buffer was the last remaining Magit buffer + that was ever displayed in the window, then that is deleted. + + +File: magit.info, Node: Automatic Refreshing of Magit Buffers, Next: Automatic Saving of File-Visiting Buffers, Prev: Quitting Windows, Up: Modes and Buffers + +4.1.4 Automatic Refreshing of Magit Buffers +------------------------------------------- + +After running a command which may change the state of the current +repository, the current Magit buffer and the corresponding status buffer +are refreshed. The status buffer can be automatically refreshed +whenever a buffer is saved to a file inside the respective repository by +adding a hook, like so: + + (with-eval-after-load 'magit-mode + (add-hook 'after-save-hook 'magit-after-save-refresh-status t)) + + Automatically refreshing Magit buffers ensures that the displayed +information is up-to-date most of the time but can lead to a noticeable +delay in big repositories. Other Magit buffers are not refreshed to +keep the delay to a minimum and also because doing so can sometimes be +undesirable. + + Buffers can also be refreshed explicitly, which is useful in buffers +that weren’t current during the last refresh and after changes were made +to the repository outside of Magit. + +‘g’ (‘magit-refresh’) + This command refreshes the current buffer if its major mode derives + from ‘magit-mode’ as well as the corresponding status buffer. + + If the option ‘magit-revert-buffers’ calls for it, then it also + reverts all unmodified buffers that visit files being tracked in + the current repository. + +‘G’ (‘magit-refresh-all’) + This command refreshes all Magit buffers belonging to the current + repository and also reverts all unmodified buffers that visit files + being tracked in the current repository. + + The file-visiting buffers are always reverted, even if + ‘magit-revert-buffers’ is nil. + + -- User Option: magit-refresh-buffer-hook + This hook is run in each Magit buffer that was refreshed during the + current refresh - normally the current buffer and the status + buffer. + + -- User Option: magit-refresh-status-buffer + When this option is non-nil, then the status buffer is + automatically refreshed after running git for side-effects, in + addition to the current Magit buffer, which is always refreshed + automatically. + + Only set this to nil after exhausting all other options to improve + performance. + + -- Function: magit-after-save-refresh-status + This function is intended to be added to ‘after-save-hook’. After + doing that the corresponding status buffer is refreshed whenever a + buffer is saved to a file inside a repository. + + Note that refreshing a Magit buffer is done by re-creating its + contents from scratch, which can be slow in large repositories. If + you are not satisfied with Magit’s performance, then you should + obviously not add this function to that hook. + + +File: magit.info, Node: Automatic Saving of File-Visiting Buffers, Next: Automatic Reverting of File-Visiting Buffers, Prev: Automatic Refreshing of Magit Buffers, Up: Modes and Buffers + +4.1.5 Automatic Saving of File-Visiting Buffers +----------------------------------------------- + +File-visiting buffers are by default saved at certain points in time. +This doesn’t guarantee that Magit buffers are always up-to-date, but, +provided one only edits files by editing them in Emacs and uses only +Magit to interact with Git, one can be fairly confident. When in doubt +or after outside changes, type ‘g’ (‘magit-refresh’) to save and refresh +explicitly. + + -- User Option: magit-save-repository-buffers + This option controls whether file-visiting buffers are saved before + certain events. + + If this is non-nil then all modified file-visiting buffers + belonging to the current repository may be saved before running + commands, before creating new Magit buffers, and before explicitly + refreshing such buffers. If this is ‘dontask’ then this is done + without user intervention. If it is ‘t’ then the user has to + confirm each save. + + +File: magit.info, Node: Automatic Reverting of File-Visiting Buffers, Prev: Automatic Saving of File-Visiting Buffers, Up: Modes and Buffers + +4.1.6 Automatic Reverting of File-Visiting Buffers +-------------------------------------------------- + +By default Magit automatically reverts buffers that are visiting files +that are being tracked in a Git repository, after they have changed on +disk. When using Magit one often changes files on disk by running Git, +i.e., "outside Emacs", making this a rather important feature. + + For example, if you discard a change in the status buffer, then that +is done by running ‘git apply --reverse ...’, and Emacs considers the +file to have "changed on disk". If Magit did not automatically revert +the buffer, then you would have to type ‘M-x revert-buffer RET RET’ in +the visiting buffer before you could continue making changes. + + -- User Option: magit-auto-revert-mode + When this mode is enabled, then buffers that visit tracked files + are automatically reverted after the visited files change on disk. + + -- User Option: global-auto-revert-mode + When this mode is enabled, then any file-visiting buffer is + automatically reverted after the visited file changes on disk. + + If you like buffers that visit tracked files to be automatically + reverted, then you might also like any buffer to be reverted, not + just those visiting tracked files. If that is the case, then + enable this mode _instead of_ ‘magit-auto-revert-mode’. + + -- User Option: magit-auto-revert-immediately + This option controls whether Magit reverts buffers immediately. + + If this is non-nil and either ‘global-auto-revert-mode’ or + ‘magit-auto-revert-mode’ is enabled, then Magit immediately reverts + buffers by explicitly calling ‘auto-revert-buffers’ after running + Git for side-effects. + + If ‘auto-revert-use-notify’ is non-nil (and file notifications are + actually supported), then ‘magit-auto-revert-immediately’ does not + have to be non-nil, because the reverts happen immediately anyway. + + If ‘magit-auto-revert-immediately’ and ‘auto-revert-use-notify’ are + both ‘nil’, then reverts happen after ‘auto-revert-interval’ + seconds of user inactivity. That is not desirable. + + -- User Option: auto-revert-use-notify + This option controls whether file notification functions should be + used. Note that this variable unfortunately defaults to ‘t’ even + on systems on which file notifications cannot be used. + + -- User Option: magit-auto-revert-tracked-only + This option controls whether ‘magit-auto-revert-mode’ only reverts + tracked files or all files that are located inside Git + repositories, including untracked files and files located inside + Git’s control directory. + + -- User Option: auto-revert-mode + The global mode ‘magit-auto-revert-mode’ works by turning on this + local mode in the appropriate buffers (but + ‘global-auto-revert-mode’ is implemented differently). You can + also turn it on or off manually, which might be necessary if Magit + does not notice that a previously untracked file now is being + tracked or vice-versa. + + -- User Option: auto-revert-stop-on-user-input + This option controls whether the arrival of user input suspends the + automatic reverts for ‘auto-revert-interval’ seconds. + + -- User Option: auto-revert-interval + This option controls how many seconds Emacs waits for before + resuming suspended reverts. + + -- User Option: auto-revert-buffer-list-filter + This option specifies an additional filter used by + ‘auto-revert-buffers’ to determine whether a buffer should be + reverted or not. + + This option is provided by Magit, which also advises + ‘auto-revert-buffers’ to respect it. Magit users who do not turn + on the local mode ‘auto-revert-mode’ themselves, are best served by + setting the value to ‘magit-auto-revert-repository-buffer-p’. + + However the default is nil, so as not to disturb users who do use + the local mode directly. If you experience delays when running + Magit commands, then you should consider using one of the + predicates provided by Magit - especially if you also use Tramp. + + Users who do turn on ‘auto-revert-mode’ in buffers in which Magit + doesn’t do that for them, should likely not use any filter. Users + who turn on ‘global-auto-revert-mode’, do not have to worry about + this option, because it is disregarded if the global mode is + enabled. + + -- User Option: auto-revert-verbose + This option controls whether Emacs reports when a buffer has been + reverted. + + The options with the ‘auto-revert-’ prefix are located in the Custom +group named ‘auto-revert’. The other, Magit-specific, options are +located in the ‘magit’ group. + +* Menu: + +* Risk of Reverting Automatically:: + + +File: magit.info, Node: Risk of Reverting Automatically, Up: Automatic Reverting of File-Visiting Buffers + +Risk of Reverting Automatically +............................... + +For the vast majority of users, automatically reverting file-visiting +buffers after they have changed on disk is harmless. + + If a buffer is modified (i.e., it contains changes that haven’t been +saved yet), then Emacs will refuse to automatically revert it. If you +save a previously modified buffer, then that results in what is seen by +Git as an uncommitted change. Git will then refuse to carry out any +commands that would cause these changes to be lost. In other words, if +there is anything that could be lost, then either Git or Emacs will +refuse to discard the changes. + + However, if you use file-visiting buffers as a sort of ad hoc +"staging area", then the automatic reverts could potentially cause data +loss. So far I have heard from only one user who uses such a workflow. + + An example: You visit some file in a buffer, edit it, and save the +changes. Then, outside of Emacs (or at least not using Magit or by +saving the buffer) you change the file on disk again. At this point the +buffer is the only place where the intermediate version still exists. +You have saved the changes to disk, but that has since been overwritten. +Meanwhile Emacs considers the buffer to be unmodified (because you have +not made any changes to it since you last saved it to the visited file) +and therefore would not object to it being automatically reverted. At +this point an Auto-Revert mode would kick in. It would check whether +the buffer is modified and since that is not the case it would revert +it. The intermediate version would be lost. (Actually you could still +get it back using the ‘undo’ command.) + + If your workflow depends on Emacs preserving the intermediate version +in the buffer, then you have to disable all Auto-Revert modes. But +please consider that such a workflow would be dangerous even without +using an Auto-Revert mode, and should therefore be avoided. If Emacs +crashes or if you quit Emacs by mistake, then you would also lose the +buffer content. There would be no autosave file still containing the +intermediate version (because that was deleted when you saved the +buffer) and you would not be asked whether you want to save the buffer +(because it isn’t modified). + + +File: magit.info, Node: Sections, Next: Transient Commands, Prev: Modes and Buffers, Up: Interface Concepts + +4.2 Sections +============ + +Magit buffers are organized into nested sections, which can be collapsed +and expanded, similar to how sections are handled in Org mode. Each +section also has a type, and some sections also have a value. For each +section type there can also be a local keymap, shared by all sections of +that type. + + Taking advantage of the section value and type, many commands operate +on the current section, or when the region is active and selects +sections of the same type, all of the selected sections. Commands that +only make sense for a particular section type (as opposed to just +behaving differently depending on the type) are usually bound in section +type keymaps. + +* Menu: + +* Section Movement:: +* Section Visibility:: +* Section Hooks:: +* Section Types and Values:: +* Section Options:: + + +File: magit.info, Node: Section Movement, Next: Section Visibility, Up: Sections + +4.2.1 Section Movement +---------------------- + +To move within a section use the usual keys (‘C-p’, ‘C-n’, ‘C-b’, ‘C-f’ +etc), whose global bindings are not shadowed. To move to another +section use the following commands. + +‘p’ (‘magit-section-backward’) + When not at the beginning of a section, then move to the beginning + of the current section. At the beginning of a section, instead + move to the beginning of the previous visible section. + +‘n’ (‘magit-section-forward’) + Move to the beginning of the next visible section. + +‘M-p’ (‘magit-section-backward-siblings’) + Move to the beginning of the previous sibling section. If there is + no previous sibling section, then move to the parent section + instead. + +‘M-n’ (‘magit-section-forward-siblings’) + Move to the beginning of the next sibling section. If there is no + next sibling section, then move to the parent section instead. + +‘^’ (‘magit-section-up’) + Move to the beginning of the parent of the current section. + + The above commands all call the hook ‘magit-section-movement-hook’. +Any of the functions listed below can be used as members of this hook. + + You might want to remove some of the functions that Magit adds using +‘add-hook’. In doing so you have to make sure you do not attempt to +remove function that haven’t even been added yet, for example: + + (with-eval-after-load 'magit-diff + (remove-hook 'magit-section-movement-hook + 'magit-hunk-set-window-start)) + + -- Variable: magit-section-movement-hook + This hook is run by all of the above movement commands, after + arriving at the destination. + + -- Function: magit-hunk-set-window-start + This hook function ensures that the beginning of the current + section is visible, provided it is a ‘hunk’ section. Otherwise, it + does nothing. + + Loading ‘magit-diff’ adds this function to the hook. + + -- Function: magit-section-set-window-start + This hook function ensures that the beginning of the current + section is visible, regardless of the section’s type. If you add + this to ‘magit-section-movement-hook’, then you must remove the + hunk-only variant in turn. + + -- Function: magit-log-maybe-show-more-commits + This hook function only has an effect in log buffers, and ‘point’ + is on the "show more" section. If that is the case, then it + doubles the number of commits that are being shown. + + Loading ‘magit-log’ adds this function to the hook. + + -- Function: magit-log-maybe-update-revision-buffer + When moving inside a log buffer, then this function updates the + revision buffer, provided it is already being displayed in another + window of the same frame. + + Loading ‘magit-log’ adds this function to the hook. + + -- Function: magit-log-maybe-update-blob-buffer + When moving inside a log buffer and another window of the same + frame displays a blob buffer, then this function instead displays + the blob buffer for the commit at point in that window. + + -- Function: magit-status-maybe-update-revision-buffer + When moving inside a status buffer, then this function updates the + revision buffer, provided it is already being displayed in another + window of the same frame. + + -- Function: magit-status-maybe-update-stash-buffer + When moving inside a status buffer, then this function updates the + stash buffer, provided it is already being displayed in another + window of the same frame. + + -- Function: magit-status-maybe-update-blob-buffer + When moving inside a status buffer and another window of the same + frame displays a blob buffer, then this function instead displays + the blob buffer for the commit at point in that window. + + -- Function: magit-stashes-maybe-update-stash-buffer + When moving inside a buffer listing stashes, then this function + updates the stash buffer, provided it is already being displayed in + another window of the same frame. + + -- User Option: magit-update-other-window-delay + Delay before automatically updating the other window. + + When moving around in certain buffers, then certain other buffers, + which are being displayed in another window, may optionally be + updated to display information about the section at point. + + When holding down a key to move by more than just one section, then + that would update that buffer for each section on the way. To + prevent that, updating the revision buffer is delayed, and this + option controls for how long. For optimal experience you might + have to adjust this delay and/or the keyboard repeat rate and delay + of your graphical environment or operating system. + + +File: magit.info, Node: Section Visibility, Next: Section Hooks, Prev: Section Movement, Up: Sections + +4.2.2 Section Visibility +------------------------ + +Magit provides many commands for changing the visibility of sections, +but all you need to get started are the next two. + +‘<TAB>’ (‘magit-section-toggle’) + Toggle the visibility of the body of the current section. + +‘C-c <TAB>’ (‘magit-section-cycle’) +‘C-<tab>’ (‘magit-section-cycle’) + Cycle the visibility of current section and its children. + + If this command is invoked using ‘C-<tab>’ and that is globally + bound to ‘tab-next’, then this command pivots to behave like that + command, and you must instead use ‘C-c TAB’ to cycle section + visibility. + + If you would like to keep using ‘C-<tab>’ to cycle section + visibility but also want to use ‘tab-bar-mode’, then you have to + prevent that mode from using this key and instead bind another key + to ‘tab-next’. Because ‘tab-bar-mode’ does not use a mode map but + instead manipulates the global map, this involves advising + ‘tab-bar--define-keys’. + +‘M-<tab>’ (‘magit-section-cycle-diffs’) + Cycle the visibility of diff-related sections in the current + buffer. + +‘S-<tab>’ (‘magit-section-cycle-global’) + Cycle the visibility of all sections in the current buffer. + +‘1’ (‘magit-section-show-level-1’) +‘2’ (‘magit-section-show-level-2’) +‘3’ (‘magit-section-show-level-3’) +‘4’ (‘magit-section-show-level-4’) + Show sections surrounding the current section up to level N. + +‘M-1’ (‘magit-section-show-level-1-all’) +‘M-2’ (‘magit-section-show-level-2-all’) +‘M-3’ (‘magit-section-show-level-3-all’) +‘M-4’ (‘magit-section-show-level-4-all’) + Show all sections up to level N. + + Some functions, which are used to implement the above commands, are +also exposed as commands themselves. By default no keys are bound to +these commands, as they are generally perceived to be much less useful. +But your mileage may vary. + + -- Command: magit-section-show + Show the body of the current section. + + -- Command: magit-section-hide + Hide the body of the current section. + + -- Command: magit-section-show-headings + Recursively show headings of children of the current section. Only + show the headings. Previously shown text-only bodies are hidden. + + -- Command: magit-section-show-children + Recursively show the bodies of children of the current section. + With a prefix argument show children down to the level of the + current section, and hide deeper children. + + -- Command: magit-section-hide-children + Recursively hide the bodies of children of the current section. + + -- Command: magit-section-toggle-children + Toggle visibility of bodies of children of the current section. + + When a buffer is first created then some sections are shown expanded +while others are not. This is hard coded. When a buffer is refreshed +then the previous visibility is preserved. The initial visibility of +certain sections can also be overwritten using the hook +‘magit-section-set-visibility-hook’. + + -- User Option: magit-section-initial-visibility-alist + This options can be used to override the initial visibility of + sections. In the future it will also be used to define the + defaults, but currently a section’s default is still hardcoded. + + The value is an alist. Each element maps a section type or lineage + to the initial visibility state for such sections. The state has + to be one of ‘show’ or ‘hide’, or a function that returns one of + these symbols. A function is called with the section as the only + argument. + + Use the command ‘magit-describe-section-briefly’ to determine a + section’s lineage or type. The vector in the output is the section + lineage and the type is the first element of that vector. + Wildcards can be used, see ‘magit-section-match’. + + -- User Option: magit-section-cache-visibility + This option controls for which sections the previous visibility + state should be restored if a section disappears and later appears + again. The value is a boolean or a list of section types. If t, + then the visibility of all sections is cached. Otherwise this is + only done for sections whose type matches one of the listed types. + + This requires that the function ‘magit-section-cached-visibility’ + is a member of ‘magit-section-set-visibility-hook’. + + -- Variable: magit-section-set-visibility-hook + This hook is run when first creating a buffer and also when + refreshing an existing buffer, and is used to determine the + visibility of the section currently being inserted. + + Each function is called with one argument, the section being + inserted. It should return ‘hide’ or ‘show’, or to leave the + visibility undefined ‘nil’. If no function decides on the + visibility and the buffer is being refreshed, then the visibility + is preserved; or if the buffer is being created, then the hard + coded default is used. + + Usually this should only be used to set the initial visibility but + not during refreshes. If ‘magit-insert-section--oldroot’ is + non-nil, then the buffer is being refreshed and these functions + should immediately return ‘nil’. + + -- User Option: magit-section-visibility-indicator + This option controls whether and how to indicate that a section can + be expanded/collapsed. + + If nil, then no visibility indicators are shown. Otherwise the + value has to have one of these two forms: + + • ‘(EXPANDABLE-BITMAP . COLLAPSIBLE-BITMAP)’ + + Both values have to be variables whose values are fringe + bitmaps. In this case every section that can be expanded or + collapsed gets an indicator in the left fringe. + + To provide extra padding around the indicator, set + ‘left-fringe-width’ in ‘magit-mode-hook’, e.g.: + + (add-hook 'magit-mode-hook (lambda () + (setq left-fringe-width 20))) + + • ‘(STRING . BOOLEAN)’ + + In this case STRING (usually an ellipsis) is shown at the end + of the heading of every collapsed section. Expanded sections + get no indicator. The cdr controls whether the appearance of + these ellipsis take section highlighting into account. Doing + so might potentially have an impact on performance, while not + doing so is kinda ugly. + + +File: magit.info, Node: Section Hooks, Next: Section Types and Values, Prev: Section Visibility, Up: Sections + +4.2.3 Section Hooks +------------------- + +Which sections are inserted into certain buffers is controlled with +hooks. This includes the status and the refs buffers. For other +buffers, e.g., log and diff buffers, this is not possible. The command +‘magit-describe-section’ can be used to see which hook (if any) was +responsible for inserting the section at point. + + For buffers whose sections can be customized by the user, a hook +variable called ‘magit-TYPE-sections-hook’ exists. This hook should be +changed using ‘magit-add-section-hook’. Avoid using ‘add-hooks’ or the +Custom interface. + + The various available section hook variables are described later in +this manual along with the appropriate "section inserter functions". + + -- Function: magit-add-section-hook hook function &optional at append + local + Add the function FUNCTION to the value of section hook HOOK. + + Add FUNCTION at the beginning of the hook list unless optional + APPEND is non-nil, in which case FUNCTION is added at the end. If + FUNCTION already is a member then move it to the new location. + + If optional AT is non-nil and a member of the hook list, then add + FUNCTION next to that instead. Add before or after AT, or replace + AT with FUNCTION depending on APPEND. If APPEND is the symbol + ‘replace’, then replace AT with FUNCTION. For any other non-nil + value place FUNCTION right after AT. If nil, then place FUNCTION + right before AT. If FUNCTION already is a member of the list but + AT is not, then leave FUNCTION where ever it already is. + + If optional LOCAL is non-nil, then modify the hook’s buffer-local + value rather than its global value. This makes the hook local by + copying the default value. That copy is then modified. + + HOOK should be a symbol. If HOOK is void, it is first set to nil. + HOOK’s value must not be a single hook function. FUNCTION should + be a function that takes no arguments and inserts one or multiple + sections at point, moving point forward. FUNCTION may choose not + to insert its section(s), when doing so would not make sense. It + should not be abused for other side-effects. + + To remove a function from a section hook, use ‘remove-hook’. + + +File: magit.info, Node: Section Types and Values, Next: Section Options, Prev: Section Hooks, Up: Sections + +4.2.4 Section Types and Values +------------------------------ + +Each section has a type, for example ‘hunk’, ‘file’, and ‘commit’. +Instances of certain section types also have a value. The value of a +section of type ‘file’, for example, is a file name. + + Users usually do not have to worry about a section’s type and value, +but knowing them can be handy at times. + +‘H’ (‘magit-describe-section’) + This command shows information about the section at point in a + separate buffer. + + -- Command: magit-describe-section-briefly + This command shows information about the section at point in the + echo area, as ‘#<magit-section VALUE [TYPE PARENT-TYPE...] + BEGINNING-END>’. + + Many commands behave differently depending on the type of the section +at point and/or somehow consume the value of that section. But that is +only one of the reasons why the same key may do something different, +depending on what section is current. + + Additionally for each section type a keymap *might* be defined, named +‘magit-TYPE-section-map’. That keymap is used as text property keymap +of all text belonging to any section of the respective type. If such a +map does not exist for a certain type, then you can define it yourself, +and it will automatically be used. + + +File: magit.info, Node: Section Options, Prev: Section Types and Values, Up: Sections + +4.2.5 Section Options +--------------------- + +This section describes options that have an effect on more than just a +certain type of sections. As you can see there are not many of those. + + -- User Option: magit-section-show-child-count + Whether to append the number of children to section headings. This + only affects sections that could benefit from this information. + + +File: magit.info, Node: Transient Commands, Next: Transient Arguments and Buffer Variables, Prev: Sections, Up: Interface Concepts + +4.3 Transient Commands +====================== + +Many Magit commands are implemented as *transient* commands. First the +user invokes a *prefix* command, which causes its *infix* arguments and +*suffix* commands to be displayed in the echo area. The user then +optionally sets some infix arguments and finally invokes one of the +suffix commands. + + This is implemented in the library ‘transient’. Earlier Magit +releases used the package ‘magit-popup’ and even earlier versions +library ‘magit-key-mode’. + + Transient is documented in *note (transient)Top::. + +‘C-x M-g’ (‘magit-dispatch’) +‘C-c g’ (‘magit-dispatch’) + This transient prefix command binds most of Magit’s other prefix + commands as suffix commands and displays them in a temporary buffer + until one of them is invoked. Invoking such a sub-prefix causes + the suffixes of that command to be bound and displayed instead of + those of ‘magit-dispatch’. + + This command is also, or especially, useful outside Magit buffers, + so Magit by default binds it to ‘C-c M-g’ in the global keymap. + ‘C-c g’ would be a better binding, but we cannot use that by + default, because that key sequence is reserved for the user. See + *note Global Bindings:: to learn more default and recommended key + bindings. + + +File: magit.info, Node: Transient Arguments and Buffer Variables, Next: Completion Confirmation and the Selection, Prev: Transient Commands, Up: Interface Concepts + +4.4 Transient Arguments and Buffer Variables +============================================ + +The infix arguments of many of Magit’s transient prefix commands cease +to have an effect once the ‘git’ command that is called with those +arguments has returned. Commands that create a commit are a good +example for this. If the user changes the arguments, then that only +affects the next invocation of a suffix command. If the same transient +prefix command is later invoked again, then the arguments are initially +reset to the default value. This default value can be set for the +current Emacs session or saved permanently, see *note (transient)Saving +Values::. It is also possible to cycle through previously used sets of +arguments using ‘C-M-p’ and ‘C-M-n’, see *note (transient)Using +History::. + + However the infix arguments of many other transient commands continue +to have an effect even after the ‘git’ command that was called with +those arguments has returned. The most important commands like this are +those that display a diff or log in a dedicated buffer. Their arguments +obviously continue to have an effect for as long as the respective diff +or log is being displayed. Furthermore the used arguments are stored in +buffer-local variables for future reference. + + For commands in the second group it isn’t always desirable to reset +their arguments to the global value when the transient prefix command is +invoked again. + + As mentioned above, it is possible to cycle through previously used +sets of arguments while a transient popup is visible. That means that +we could always reset the infix arguments to the default because the set +of arguments that is active in the existing buffer is only a few ‘C-M-p’ +away. Magit can be configured to behave like that, but because I expect +that most users would not find that very convenient, it is not the +default. + + Also note that it is possible to change the diff and log arguments +used in the current buffer (including the status buffer, which contains +both diff and log sections) using the respective "refresh" transient +prefix commands on ‘D’ and ‘L’. (‘d’ and ‘l’ on the other hand are +intended to change *what* diff or log is being displayed. It is +possible to also change *how* the diff or log is being displayed at the +same time, but if you only want to do the latter, then you should use +the refresh variants.) Because these secondary diff and log transient +prefixes are about *changing* the arguments used in the current buffer, +they *always* start out with the set of arguments that are currently in +effect in that buffer. + + Some commands are usually invoked directly even though they can also +be invoked as the suffix of a transient prefix command. Most +prominently ‘magit-show-commit’ is usually invoked by typing ‘RET’ while +point is on a commit in a log, but it can also be invoked from the +‘magit-diff’ transient prefix. + + When such a command is invoked directly, then it is important to +reuse the arguments as specified by the respective buffer-local values, +instead of using the default arguments. Imagine you press ‘RET’ in a +log to display the commit at point in a different buffer and then use +‘D’ to change how the diff is displayed in that buffer. And then you +press ‘RET’ on another commit to show that instead and the diff +arguments are reset to the default. Not cool; so Magit does not do that +by default. + + -- User Option: magit-prefix-use-buffer-arguments + This option controls whether the infix arguments initially shown in + certain transient prefix commands are based on the arguments that + are currently in effect in the buffer that their suffixes update. + + The ‘magit-diff’ and ‘magit-log’ transient prefix commands are + affected by this option. + + -- User Option: magit-direct-use-buffer-arguments + This option controls whether certain commands, when invoked + directly (i.e., not as the suffix of a transient prefix command), + use the arguments that are currently active in the buffer that they + are about to update. The alternative is to use the default value + for these arguments, which might change the arguments that are used + in the buffer. + +Valid values for both of the above options are: + + • ‘always’: Always use the set of arguments that is currently active + in the respective buffer, provided that buffer exists of course. + • ‘selected’ or ‘t’: Use the set of arguments from the respective + buffer, but only if it is displayed in a window of the current + frame. This is the default for both variables. + • ‘current’: Use the set of arguments from the respective buffer, but + only if it is the current buffer. + • ‘never’: Never use the set of arguments from the respective buffer. + +I am afraid it gets more complicated still: + + • The global diff and log arguments are set for each supported mode + individually. The diff arguments for example have different values + in ‘magit-diff-mode’, ‘magit-revision-mode’, + ‘magit-merge-preview-mode’ and ‘magit-status-mode’ buffers. + Setting or saving the value for one mode does not change the value + for other modes. The history however is shared. + + • When ‘magit-show-commit’ is invoked directly from a log buffer, + then the file filter is picked up from that buffer, not from the + revision buffer or the mode’s global diff arguments. + + • Even though they are suffixes of the diff prefix + ‘magit-show-commit’ and ‘magit-stash-show’ do not use the diff + buffer used by the diff commands, instead they use the dedicated + revision and stash buffers. + + At the time you invoke the diff prefix it is unknown to Magit which + of the suffix commands you are going to invoke. While not certain, + more often than not users invoke one of the commands that use the + diff buffer, so the initial infix arguments are those used in that + buffer. However if you invoke one of these commands directly, then + Magit knows that it should use the arguments from the revision + resp. stash buffer. + + • The log prefix also features reflog commands, but these commands do + not use the log arguments. + + • If ‘magit-show-refs’ is invoked from a ‘magit-refs-mode’ buffer, + then it acts as a refresh prefix and therefore unconditionally uses + the buffer’s arguments as initial arguments. If it is invoked + elsewhere with a prefix argument, then it acts as regular prefix + and therefore respects ‘magit-prefix-use-buffer-arguments’. If it + is invoked elsewhere without a prefix argument, then it acts as a + direct command and therefore respects + ‘magit-direct-use-buffer-arguments’. + + +File: magit.info, Node: Completion Confirmation and the Selection, Next: Mouse Support, Prev: Transient Arguments and Buffer Variables, Up: Interface Concepts + +4.5 Completion, Confirmation and the Selection +============================================== + +* Menu: + +* Action Confirmation:: +* Completion and Confirmation:: +* The Selection:: +* The hunk-internal region:: +* Support for Completion Frameworks:: +* Additional Completion Options:: + + +File: magit.info, Node: Action Confirmation, Next: Completion and Confirmation, Up: Completion Confirmation and the Selection + +4.5.1 Action Confirmation +------------------------- + +By default many actions that could potentially lead to data loss have to +be confirmed. This includes many very common actions, so this can +quickly become annoying. Many of these actions can be undone and if you +have thought about how to undo certain mistakes, then it should be safe +to disable confirmation for the respective actions. + + The option ‘magit-no-confirm’ can be used to tell Magit to perform +certain actions without the user having to confirm them. Note that +while this option can only be used to disable confirmation for a +specific set of actions, the next section explains another way of +telling Magit to ask fewer questions. + + -- User Option: magit-no-confirm + The value of this option is a list of symbols, representing actions + that do not have to be confirmed by the user before being carried + out. + + By default many potentially dangerous commands ask the user for + confirmation. Each of the below symbols stands for an action + which, when invoked unintentionally or without being fully aware of + the consequences, could lead to tears. In many cases there are + several commands that perform variations of a certain action, so we + don’t use the command names but more generic symbols. + + • Applying changes: + + • ‘discard’ Discarding one or more changes (i.e., hunks or + the complete diff for a file) loses that change, + obviously. + + • ‘reverse’ Reverting one or more changes can usually be + undone by reverting the reversion. + + • ‘stage-all-changes’, ‘unstage-all-changes’ When there are + both staged and unstaged changes, then un-/staging + everything would destroy that distinction. Of course + that also applies when un-/staging a single change, but + then less is lost and one does that so often that having + to confirm every time would be unacceptable. + + • Files: + + • ‘delete’ When a file that isn’t yet tracked by Git is + deleted, then it is completely lost, not just the last + changes. Very dangerous. + + • ‘trash’ Instead of deleting a file it can also be move to + the system trash. Obviously much less dangerous than + deleting it. + + Also see option ‘magit-delete-by-moving-to-trash’. + + • ‘resurrect’ A deleted file can easily be resurrected by + "deleting" the deletion, which is done using the same + command that was used to delete the same file in the + first place. + + • ‘untrack’ Untracking a file can be undone by tracking it + again. + + • ‘rename’ Renaming a file can easily be undone. + + • Sequences: + + • ‘reset-bisect’ Aborting (known to Git as "resetting") a + bisect operation loses all information collected so far. + + • ‘abort-cherry-pick’ Aborting a cherry-pick throws away + all conflict resolutions which have already been carried + out by the user. + + • ‘abort-revert’ Aborting a revert throws away all conflict + resolutions which have already been carried out by the + user. + + • ‘abort-rebase’ Aborting a rebase throws away all already + modified commits, but it’s possible to restore those from + the reflog. + + • ‘abort-merge’ Aborting a merge throws away all conflict + resolutions which have already been carried out by the + user. + + • ‘merge-dirty’ Merging with a dirty worktree can make it + hard to go back to the state before the merge was + initiated. + + • References: + + • ‘delete-unmerged-branch’ Once a branch has been deleted, + it can only be restored using low-level recovery tools + provided by Git. And even then the reflog is gone. The + user always has to confirm the deletion of a branch by + accepting the default choice (or selecting another + branch), but when a branch has not been merged yet, also + make sure the user is aware of that. + + • ‘delete-pr-remote’ When deleting a branch that was + created from a pull-request and if no other branches + still exist on that remote, then ‘magit-branch-delete’ + offers to delete the remote as well. This should be safe + because it only happens if no other refs exist in the + remotes namespace, and you can recreate the remote if + necessary. + + • ‘drop-stashes’ Dropping a stash is dangerous because Git + stores stashes in the reflog. Once a stash is removed, + there is no going back without using low-level recovery + tools provided by Git. When a single stash is dropped, + then the user always has to confirm by accepting the + default (or selecting another). This action only + concerns the deletion of multiple stashes at once. + + • Publishing: + + • ‘set-and-push’ When pushing to the upstream or the + push-remote and that isn’t actually configured yet, then + the user can first set the target. If s/he confirms the + default too quickly, then s/he might end up pushing to + the wrong branch and if the remote repository is + configured to disallow fixing such mistakes, then that + can be quite embarrassing and annoying. + + • Edit published history: + + Without adding these symbols here, you will be warned before + editing commits that have already been pushed to one of the + branches listed in ‘magit-published-branches’. + + • ‘amend-published’ Affects most commands that amend to + "HEAD". + + • ‘rebase-published’ Affects commands that perform + interactive rebases. This includes commands from the + commit transient that modify a commit other than "HEAD", + namely the various fixup and squash variants. + + • ‘edit-published’ Affects the commands + ‘magit-edit-line-commit’ and + ‘magit-diff-edit-hunk-commit’. These two commands make + it quite easy to accidentally edit a published commit, so + you should think twice before configuring them not to ask + for confirmation. + + To disable confirmation completely, add all three symbols here + or set ‘magit-published-branches’ to ‘nil’. + + • Various: + + • ‘stash-apply-3way’ When a stash cannot be applied using + ‘git stash apply’, then Magit uses ‘git apply’ instead, + possibly using the ‘--3way’ argument, which isn’t always + perfectly safe. See also ‘magit-stash-apply’. + + • ‘kill-process’ There seldom is a reason to kill a + process. + + • Global settings: + + Instead of adding all of the above symbols to the value of + this option, you can also set it to the atom ‘t’, which has + the same effect as adding all of the above symbols. Doing + that most certainly is a bad idea, especially because other + symbols might be added in the future. So even if you don’t + want to be asked for confirmation for any of these actions, + you are still better of adding all of the respective symbols + individually. + + When ‘magit-wip-before-change-mode’ is enabled, then the + following actions can be undone fairly easily: ‘discard’, + ‘reverse’, ‘stage-all-changes’, and ‘unstage-all-changes’. If + and only if this mode is enabled, then ‘safe-with-wip’ has the + same effect as adding all of these symbols individually. + + +File: magit.info, Node: Completion and Confirmation, Next: The Selection, Prev: Action Confirmation, Up: Completion Confirmation and the Selection + +4.5.2 Completion and Confirmation +--------------------------------- + +Many Magit commands ask the user to select from a list of possible +things to act on, while offering the most likely choice as the default. +For many of these commands the default is the thing at point, provided +that it actually is a valid thing to act on. For many commands that act +on a branch, the current branch serves as the default if there is no +branch at point. + + These commands combine asking for confirmation and asking for a +target to act on into a single action. The user can confirm the default +target using ‘RET’ or abort using ‘C-g’. This is similar to a +‘y-or-n-p’ prompt, but the keys to confirm or abort differ. + + At the same time the user is also given the opportunity to select +another target, which is useful because for some commands and/or in some +situations you might want to select the action before selecting the +target by moving to it. + + However you might find that for some commands you always want to use +the default target, if any, or even that you want the command to act on +the default without requiring any confirmation at all. The option +‘magit-dwim-selection’ can be used to configure certain commands to that +effect. + + Note that when the region is active then many commands act on the +things that are selected using a mechanism based on the region, in many +cases after asking for confirmation. This region-based mechanism is +called the "selection" and is described in detail in the next section. +When a selection exists that is valid for the invoked command, then that +command never offers to act on something else, and whether it asks for +confirmation is not controlled by this option. + + Also note that Magit asks for confirmation of certain actions that +are not coupled with completion (or the selection). Such dialogs are +also not affected by this option and are described in the previous +section. + + -- User Option: magit-dwim-selection + This option can be used to tell certain commands to use the thing at +point instead of asking the user to select a candidate to act on, with +or without confirmation. + + The value has the form ‘((COMMAND nil|PROMPT DEFAULT)...)’. + + • COMMAND is the command that should not prompt for a choice. To + have an effect, the command has to use the function + ‘magit-completing-read’ or a utility function which in turn uses + that function. + + • If the command uses ‘magit-completing-read’ multiple times, then + PROMPT can be used to only affect one of these uses. PROMPT, if + non-nil, is a regular expression that is used to match against the + PROMPT argument passed to ‘magit-completing-read’. + + • DEFAULT specifies how to use the default. If it is ‘t’, then the + DEFAULT argument passed to ‘magit-completing-read’ is used without + confirmation. If it is ‘ask’, then the user is given a chance to + abort. DEFAULT can also be ‘nil’, in which case the entry has no + effect. + + +File: magit.info, Node: The Selection, Next: The hunk-internal region, Prev: Completion and Confirmation, Up: Completion Confirmation and the Selection + +4.5.3 The Selection +------------------- + +If the region is active, then many Magit commands act on the things that +are selected using a mechanism based on the region instead of one single +thing. When the region is not active, then these commands act on the +thing at point or read a single thing to act on. This is described in +the previous section — this section only covers how multiple things are +selected, how that is visualized, and how certain commands behave when +that is the case. + + Magit’s mechanism for selecting multiple things, or rather sections +that represent these things, is based on the Emacs region, but the area +that Magit considers to be selected is typically larger than the region +and additional restrictions apply. + + Magit makes a distinction between a region that qualifies as forming +a valid Magit selection and a region that does not. If the region does +not qualify, then it is displayed as it is in other Emacs buffers. If +the region does qualify as a Magit selection, then the selection is +always visualized, while the region itself is only visualized if it +begins and ends on the same line. + + For a region to qualify as a Magit selection, it must begin in the +heading of one section and end in the heading of a sibling section. +Note that if the end of the region is at the very beginning of section +heading (i.e., at the very beginning of a line) then that section is +considered to be *inside* the selection. + + This is not consistent with how the region is normally treated in +Emacs — if the region ends at the beginning of a line, then that line is +outside the region. Due to how Magit visualizes the selection, it +should be obvious that this difference exists. + + Not every command acts on every valid selection. Some commands do +not even consider the location of point, others may act on the section +at point but not support acting on the selection, and even commands that +do support the selection of course only do so if it selects things that +they can act on. + + This is the main reason why the selection must include the section at +point. Even if a selection exists, the invoked command may disregard +it, in which case it may act on the current section only. It is much +safer to only act on the current section but not the other selected +sections than it is to act on the current section *instead* of the +selected sections. The latter would be much more surprising and if the +current section always is part of the selection, then that cannot +happen. + + -- Variable: magit-keep-region-overlay + This variable controls whether the region is visualized as usual + even when a valid Magit selection or a hunk-internal region exists. + See the doc-string for more information. + + +File: magit.info, Node: The hunk-internal region, Next: Support for Completion Frameworks, Prev: The Selection, Up: Completion Confirmation and the Selection + +4.5.4 The hunk-internal region +------------------------------ + +Somewhat related to the Magit selection described in the previous +section is the hunk-internal region. + + Like the selection, the hunk-internal region is based on the Emacs +region but causes that region to not be visualized as it would in other +Emacs buffers, and includes the line on which the region ends even if it +ends at the very beginning of that line. + + Unlike the selection, which is based on a region that must begin in +the heading of one section and ends in the section of a sibling section, +the hunk-internal region must begin inside the *body* of a hunk section +and end in the body of the *same* section. + + The hunk-internal region is honored by "apply" commands, which can, +among other targets, act on a hunk. If the hunk-internal region is +active, then such commands act only on the marked part of the hunk +instead of on the complete hunk. + + +File: magit.info, Node: Support for Completion Frameworks, Next: Additional Completion Options, Prev: The hunk-internal region, Up: Completion Confirmation and the Selection + +4.5.5 Support for Completion Frameworks +--------------------------------------- + +The built-in option ‘completing-read-function’ specifies the low-level +function used by ‘completing-read’ to ask a user to select from a list +of choices. Its default value is ‘completing-read-default’. +Alternative completion frameworks typically activate themselves by +substituting their own implementation. + + Mostly for historic reasons Magit provides a similar option named +‘magit-completing-read-function’, which only controls the low-level +function used by ‘magit-completing-read’. This option also makes it +possible to use a different completing mechanism for Magit than for the +rest of Emacs, but doing that is not recommend. + + You most likely don’t have to customize the magit-specific option to +use an alternative completion framework. For example, if you enable +‘ivy-mode’, then Magit will respect that, and if you enable ‘helm-mode’, +then you are done too. + + However if you want to use Ido, then ‘ido-mode’ won’t do the trick. +You will also have to install the ‘ido-completing-read+’ package and use +‘magit-ido-completing-read’ as ‘magit-completing-read-function’. + + -- User Option: magit-completing-read-function + The value of this variable is the low-level function used to + perform completion by code that uses ‘magit-completing-read’ (as + opposed to the built-in ‘completing-read’). + + The default value, ‘magit-builtin-completing-read’, is suitable for + the standard completion mechanism, ‘ivy-mode’, and ‘helm-mode’ at + least. + + The built-in ‘completing-read’ and ‘completing-read-default’ are + *not* suitable to be used here. ‘magit-builtin-completing-read’ + performs some additional work, and any function used in its place + has to do the same. + + -- Function: magit-builtin-completing-read prompt choices &optional + predicate require-match initial-input hist def + This function performs completion using the built-in + ‘completing-read’ and does some additional magit-specific work. + + -- Function: magit-ido-completing-read prompt choices &optional + predicate require-match initial-input hist def + This function performs completion using ‘ido-completing-read+’ from + the package by the same name (which you have to explicitly install) + and does some additional magit-specific work. + + We have to use ‘ido-completing-read+’ instead of the + ‘ido-completing-read’ that comes with Ido itself, because the + latter, while intended as a drop-in replacement, cannot serve that + purpose because it violates too many of the implicit conventions. + + -- Function: magit-completing-read prompt choices &optional predicate + require-match initial-input hist def fallback + This is the function that Magit commands use when they need the + user to select a single thing to act on. The arguments have the + same meaning as for ‘completing-read’, except for FALLBACK, which + is unique to this function and is described below. + + Instead of asking the user to choose from a list of possible + candidates, this function may just return the default specified by + DEF, with or without requiring user confirmation. Whether that is + the case depends on PROMPT, ‘this-command’ and + ‘magit-dwim-selection’. See the documentation of the latter for + more information. + + If it does read a value in the minibuffer, then this function acts + similar to ‘completing-read’, except for the following: + + • COLLECTION must be a list of choices. A function is not + supported. + + • If REQUIRE-MATCH is ‘nil’ and the user exits without a choice, + then ‘nil’ is returned instead of an empty string. + + • If REQUIRE-MATCH is non-nil and the users exits without a + choice, an user-error is raised. + + • FALLBACK specifies a secondary default that is only used if + the primary default DEF is ‘nil’. The secondary default is + not subject to ‘magit-dwim-selection’ — if DEF is ‘nil’ but + FALLBACK is not, then this function always asks the user to + choose a candidate, just as if both defaults were ‘nil’. + + • ‘format-prompt’ is called on PROMPT and DEF (or FALLBACK if + DEF is ‘nil’). This appends ": " to the prompt and may also + add the default to the prompt, using the format specified by + ‘minibuffer-default-prompt-format’ and depending on + ‘magit-completing-read-default-prompt-predicate’. + + +File: magit.info, Node: Additional Completion Options, Prev: Support for Completion Frameworks, Up: Completion Confirmation and the Selection + +4.5.6 Additional Completion Options +----------------------------------- + + -- User Option: magit-list-refs-sortby + For many commands that read a ref or refs from the user, the value + of this option can be used to control the order of the refs. Valid + values include any key accepted by the ‘--sort’ flag of ‘git + for-each-ref’. By default, refs are sorted alphabetically by their + full name (e.g., "refs/heads/master"). + + +File: magit.info, Node: Mouse Support, Next: Running Git, Prev: Completion Confirmation and the Selection, Up: Interface Concepts + +4.6 Mouse Support +================= + +Double clicking on a section heading toggles the visibility of its body, +if any. Likewise clicking in the left fringe toggles the visibility of +the appropriate section. + + A context menu is provided but has to be enabled explicitly. In +Emacs 28 and greater, enable the global mode ‘context-menu-mode’. If +you use an older Emacs release, set +‘magit-section-show-context-menu-for-emacs<28’. + + +File: magit.info, Node: Running Git, Prev: Mouse Support, Up: Interface Concepts + +4.7 Running Git +=============== + +* Menu: + +* Viewing Git Output:: +* Git Process Status:: +* Running Git Manually:: +* Git Executable:: +* Global Git Arguments:: + + +File: magit.info, Node: Viewing Git Output, Next: Git Process Status, Up: Running Git + +4.7.1 Viewing Git Output +------------------------ + +Magit runs Git either for side-effects (e.g., when pushing) or to get +some value (e.g., the name of the current branch). + + When Git is run for side-effects, the process output is logged in a +per-repository log buffer, which can be consulted using the +‘magit-process’ command when things don’t go as expected. + + The output/errors for up to ‘magit-process-log-max’ Git commands are +retained. + +‘$’ (‘magit-process’) + This commands displays the process buffer for the current + repository. + + Inside that buffer, the usual key bindings for navigating and showing +sections are available. There is one additional command. + +‘k’ (‘magit-process-kill’) + This command kills the process represented by the section at point. + + -- Variable: magit-git-debug + This option controls whether additional reporting of git errors is + enabled. + + Magit basically calls git for one of these two reasons: for + side-effects or to do something with its standard output. + + When git is run for side-effects then its output, including error + messages, go into the process buffer which is shown when using ‘$’. + + When git’s output is consumed in some way, then it would be too + expensive to also insert it into this buffer, but when this option + is non-nil and git returns with a non-zero exit status, then at + least its standard error is inserted into this buffer. + + This is only intended for debugging purposes. Do not enable this + permanently, that would negatively affect performance. + + This is only intended for debugging purposes. Do not enable this + permanently, that would negatively affect performance. Also note + that just because git exits with a non-zero exit status and prints + an error message that usually doesn’t mean that it is an error as + far as Magit is concerned, which is another reason we usually hide + these error messages. Whether some error message is relevant in + the context of some unexpected behavior has to be judged on a case + by case basis. + + The command ‘magit-toggle-git-debug’ changes the value of this + variable. + + -- Variable: magit-process-extreme-logging + This option controls whether ‘magit-process-file’ logs to the + ‘*Messages*’ buffer. + + Only intended for temporary use when you try to figure out how + Magit uses Git behind the scene. Output that normally goes to the + magit-process buffer continues to go there. Not all output goes to + either of these two buffers. + + +File: magit.info, Node: Git Process Status, Next: Running Git Manually, Prev: Viewing Git Output, Up: Running Git + +4.7.2 Git Process Status +------------------------ + +When a Git process is running for side-effects, Magit displays an +indicator in the mode line, using the ‘magit-mode-line-process’ face. + + If the Git process exits successfully, the process indicator is +removed from the mode line immediately. + + In the case of a Git error, the process indicator is not removed, but +is instead highlighted with the ‘magit-mode-line-process-error’ face, +and the error details from the process buffer are provided as a tooltip +for mouse users. This error indicator persists in the mode line until +the next magit buffer refresh. + + If you do not wish process errors to be indicated in the mode line, +customize the ‘magit-process-display-mode-line-error’ user option. + + Process errors are additionally indicated at the top of the status +buffer. + + +File: magit.info, Node: Running Git Manually, Next: Git Executable, Prev: Git Process Status, Up: Running Git + +4.7.3 Running Git Manually +-------------------------- + +While Magit provides many Emacs commands to interact with Git, it does +not cover everything. In those cases your existing Git knowledge will +come in handy. Magit provides some commands for running arbitrary Git +commands by typing them into the minibuffer, instead of having to switch +to a shell. + +‘!’ (‘magit-run’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + +‘! !’ (‘magit-git-command-topdir’) + This command reads a command from the user and executes it in the + top-level directory of the current working tree. + + The string "git " is used as initial input when prompting the user + for the command. It can be removed to run another command. + +‘:’ (‘magit-git-command’) +‘! p’ + This command reads a command from the user and executes it in + ‘default-directory’. With a prefix argument the command is + executed in the top-level directory of the current working tree + instead. + + The string "git " is used as initial input when prompting the user + for the command. It can be removed to run another command. + +‘! s’ (‘magit-shell-command-topdir’) + This command reads a command from the user and executes it in the + top-level directory of the current working tree. + +‘! S’ (‘magit-shell-command’) + This command reads a command from the user and executes it in + ‘default-directory’. With a prefix argument the command is + executed in the top-level directory of the current working tree + instead. + + -- User Option: magit-shell-command-verbose-prompt + Whether the prompt, used by the above commands when reading a shell + command, shows the directory in which it will be run. + + These suffix commands start external gui tools. + +‘! k’ (‘magit-run-gitk’) + This command runs ‘gitk’ in the current repository. + +‘! a’ (‘magit-run-gitk-all’) + This command runs ‘gitk --all’ in the current repository. + +‘! b’ (‘magit-run-gitk-branches’) + This command runs ‘gitk --branches’ in the current repository. + +‘! g’ (‘magit-run-git-gui’) + This command runs ‘git gui’ in the current repository. + +‘! m’ (‘magit-git-mergetool’) + This command runs ‘git mergetool --gui’ in the current repository. + + With a prefix argument this acts as a transient prefix command, + allowing the user to select the mergetool and change some settings. + + +File: magit.info, Node: Git Executable, Next: Global Git Arguments, Prev: Running Git Manually, Up: Running Git + +4.7.4 Git Executable +-------------------- + +When Magit calls Git, then it may do so using the absolute path to the +‘git’ executable, or using just its name. + + When running ‘git’ locally and the ‘system-type’ is ‘windows-nt’ (any +Windows version) or ‘darwin’ (macOS) then ‘magit-git-executable’ is set +to an absolute path when Magit is loaded. + + On Windows it is necessary to use an absolute path because Git comes +with several wrapper scripts for the actual ‘git’ binary, which are also +placed on ‘$PATH’, and using one of these wrappers instead of the binary +would degrade performance horribly. For some macOS users using just the +name of the executable also performs horribly, so we avoid doing that on +that platform as well. On other platforms, using just the name seems to +work just fine. + + Using an absolute path when running ‘git’ on a remote machine over +Tramp, would be problematic to use an absolute path that is suitable on +the local machine, so a separate option is used to control the name or +path that is used on remote machines. + + -- User Option: magit-git-executable + The ‘git’ executable used by Magit on the local host. This should + be either the absolute path to the executable, or the string "git" + to let Emacs find the executable itself, using the standard + mechanism for doing such things. + + -- User Option: magit-remote-git-executable + The ‘git’ executable used by Magit on remote machines over Tramp. + Normally this should be just the string "git". Consider + customizing ‘tramp-remote-path’ instead of this option. + + If Emacs is unable to find the correct executable, then you can work +around that by explicitly setting the value of one of these two options. +Doing that should be considered a kludge; it is better to make sure that +the order in ‘exec-path’ or ‘tramp-remote-path’ is correct. + + Note that ‘exec-path’ is set based on the value of the ‘PATH’ +environment variable that is in effect when Emacs is started. If you +set ‘PATH’ in your shell’s init files, then that only has an effect on +Emacs if you start it from that shell (because the environment of a +process is only passed to its child processes, not to arbitrary other +processes). If that is not how you start Emacs, then the +‘exec-path-from-shell’ package can help; though honestly I consider that +a kludge too. + + The command ‘magit-debug-git-executable’ can be useful to find out +where Emacs is searching for ‘git’. + +‘M-x magit-debug-git-executable’ + This command displays a buffer with information about + ‘magit-git-executable’ and ‘magit-remote-git-executable’. + +‘M-x magit-version’ + This command shows the currently used versions of Magit, Git, and + Emacs in the echo area. Non-interactively this just returns the + Magit version. + + +File: magit.info, Node: Global Git Arguments, Prev: Git Executable, Up: Running Git + +4.7.5 Global Git Arguments +-------------------------- + + -- User Option: magit-git-global-arguments + The arguments set here are used every time the git executable is + run as a subprocess. They are placed right after the executable + itself and before the git command - as in ‘git HERE... COMMAND + REST’. For valid arguments see *note (gitman)git::. + + Be careful what you add here, especially if you are using Tramp to + connect to servers with ancient Git versions. Never remove + anything that is part of the default value, unless you really know + what you are doing. And think very hard before adding something; + it will be used every time Magit runs Git for any purpose. + + +File: magit.info, Node: Inspecting, Next: Manipulating, Prev: Interface Concepts, Up: Top + +5 Inspecting +************ + +The functionality provided by Magit can be roughly divided into three +groups: inspecting existing data, manipulating existing data or adding +new data, and transferring data. Of course that is a rather crude +distinction that often falls short, but it’s more useful than no +distinction at all. This section is concerned with inspecting data, the +next two with manipulating and transferring it. Then follows a section +about miscellaneous functionality, which cannot easily be fit into this +distinction. + + Of course other distinctions make sense too, e.g., Git’s distinction +between porcelain and plumbing commands, which for the most part is +equivalent to Emacs’ distinction between interactive commands and +non-interactive functions. All of the sections mentioned before are +mainly concerned with the porcelain – Magit’s plumbing layer is +described later. + +* Menu: + +* Status Buffer:: +* Repository List:: +* Logging:: +* Diffing:: +* Ediffing:: +* References Buffer:: +* Bisecting:: +* Visiting Files and Blobs:: +* Blaming:: + + +File: magit.info, Node: Status Buffer, Next: Repository List, Up: Inspecting + +5.1 Status Buffer +================= + +While other Magit buffers contain, e.g., one particular diff or one +particular log, the status buffer contains the diffs for staged and +unstaged changes, logs for unpushed and unpulled commits, lists of +stashes and untracked files, and information related to the current +branch. + + During certain incomplete operations – for example when a merge +resulted in a conflict – additional information is displayed that helps +proceeding with or aborting the operation. + + The command ‘magit-status’ displays the status buffer belonging to +the current repository in another window. This command is used so often +that it should be bound globally. We recommend using ‘C-x g’: + + (global-set-key (kbd "C-x g") 'magit-status) + +‘C-x g’ (‘magit-status’) + When invoked from within an existing Git repository, then this + command shows the status of that repository in a buffer. + + If the current directory isn’t located within a Git repository, + then this command prompts for an existing repository or an + arbitrary directory, depending on the option + ‘magit-repository-directories’, and the status for the selected + repository is shown instead. + + • If that option specifies any existing repositories, then the + user is asked to select one of them. + + • Otherwise the user is asked to select an arbitrary directory + using regular file-name completion. If the selected directory + is the top-level directory of an existing working tree, then + the status buffer for that is shown. + + • Otherwise the user is offered to initialize the selected + directory as a new repository. After creating the repository + its status buffer is shown. + + These fallback behaviors can also be forced using one or more + prefix arguments: + + • With two prefix arguments (or more precisely a numeric prefix + value of 16 or greater) an arbitrary directory is read, which + is then acted on as described above. The same could be + accomplished using the command ‘magit-init’. + + • With a single prefix argument an existing repository is read + from the user, or if no repository can be found based on the + value of ‘magit-repository-directories’, then the behavior is + the same as with two prefix arguments. + + -- User Option: magit-repository-directories + List of directories that are Git repositories or contain Git + repositories. + + Each element has the form ‘(DIRECTORY . DEPTH)’. DIRECTORY has to + be a directory or a directory file-name, a string. DEPTH, an + integer, specifies the maximum depth to look for Git repositories. + If it is 0, then only add DIRECTORY itself. + + This option controls which repositories are being listed by + ‘magit-list-repositories’. It also affects ‘magit-status’ (which + see) in potentially surprising ways (see above). + + -- Command: magit-status-quick + This command is an alternative to ‘magit-status’ that usually + avoids refreshing the status buffer. + + If the status buffer of the current Git repository exists but isn’t + being displayed in the selected frame, then it is displayed without + being refreshed. + + If the status buffer is being displayed in the selected frame, then + this command refreshes it. + + Prefix arguments have the same meaning as for ‘magit-status’, and + additionally cause the buffer to be refresh. + + To use this command add this to your init file: + + (global-set-key (kbd "C-x g") 'magit-status-quick). + + If you do that and then for once want to redisplay the buffer and + also immediately refresh it, then type ‘C-x g’ followed by ‘g’. + + A possible alternative command is + ‘magit-display-repository-buffer’. It supports displaying any + existing Magit buffer that belongs to the current repository; not + just the status buffer. + + -- Command: ido-enter-magit-status + From an Ido prompt used to open a file, instead drop into + ‘magit-status’. This is similar to ‘ido-magic-delete-char’, which, + despite its name, usually causes a Dired buffer to be created. + + To make this command available, use something like: + + (add-hook 'ido-setup-hook + (lambda () + (define-key ido-completion-map + (kbd \"C-x g\") 'ido-enter-magit-status))) + + Starting with Emacs 25.1 the Ido keymaps are defined just once + instead of every time Ido is invoked, so now you can modify it like + pretty much every other keymap: + + (define-key ido-common-completion-map + (kbd \"C-x g\") 'ido-enter-magit-status) + +* Menu: + +* Status Sections:: +* Status Header Sections:: +* Status Module Sections:: +* Status Options:: + + +File: magit.info, Node: Status Sections, Next: Status Header Sections, Up: Status Buffer + +5.1.1 Status Sections +--------------------- + +The contents of status buffers is controlled using the hook +‘magit-status-sections-hook’. See *note Section Hooks:: to learn about +such hooks and how to customize them. + + -- User Option: magit-status-sections-hook + Hook run to insert sections into a status buffer. + + The first function on that hook by default is +‘magit-insert-status-headers’; it is described in the next section. By +default the following functions are also members of that hook: + + -- Function: magit-insert-merge-log + Insert section for the on-going merge. Display the heads that are + being merged. If no merge is in progress, do nothing. + + -- Function: magit-insert-rebase-sequence + Insert section for the on-going rebase sequence. If no such + sequence is in progress, do nothing. + + -- Function: magit-insert-am-sequence + Insert section for the on-going patch applying sequence. If no + such sequence is in progress, do nothing. + + -- Function: magit-insert-sequencer-sequence + Insert section for the on-going cherry-pick or revert sequence. If + no such sequence is in progress, do nothing. + + -- Function: magit-insert-bisect-output + While bisecting, insert section with output from ‘git bisect’. + + -- Function: magit-insert-bisect-rest + While bisecting, insert section visualizing the bisect state. + + -- Function: magit-insert-bisect-log + While bisecting, insert section logging bisect progress. + + -- Function: magit-insert-untracked-files + Maybe insert a list or tree of untracked files. + + Do so depending on the value of ‘status.showUntrackedFiles’. Note + that even if the value is ‘all’, Magit still initially only shows + directories. But the directory sections can then be expanded using + ‘TAB’. + + -- Function: magit-insert-unstaged-changes + Insert section showing unstaged changes. + + -- Function: magit-insert-staged-changes + Insert section showing staged changes. + + -- Function: magit-insert-stashes &optional ref heading + Insert the ‘stashes’ section showing reflog for "refs/stash". If + optional REF is non-nil show reflog for that instead. If optional + HEADING is non-nil use that as section heading instead of + "Stashes:". + + -- Function: magit-insert-unpulled-from-upstream + Insert section showing commits that haven’t been pulled from the + upstream branch yet. + + -- Function: magit-insert-unpulled-from-pushremote + Insert section showing commits that haven’t been pulled from the + push-remote branch yet. + + -- Function: magit-insert-unpushed-to-upstream + Insert section showing commits that haven’t been pushed to the + upstream yet. + + -- Function: magit-insert-unpushed-to-pushremote + Insert section showing commits that haven’t been pushed to the + push-remote yet. + + The following functions can also be added to the above hook: + + -- Function: magit-insert-tracked-files + Insert a tree of tracked files. + + -- Function: magit-insert-ignored-files + Insert a tree of ignored files. Its possible to limit the logs in + the current buffer to a certain directory using ‘D = f <DIRECTORY> + RET g’. If you do that, then that that also affects this command. + + The log filter can be used to limit to multiple files. In that + case this function only respects the first of the files and only if + it is a directory. + + -- Function: magit-insert-skip-worktree-files + Insert a tree of skip-worktree files. If the first element of + ‘magit-buffer-diff-files’ is a directory, then limit the list to + files below that. The value of that variable can be set using ‘D + -- DIRECTORY RET g’. + + -- Function: magit-insert-assumed-unchanged-files + Insert a tree of files that are assumed to be unchanged. If the + first element of ‘magit-buffer-diff-files’ is a directory, then + limit the list to files below that. The value of that variable can + be set using ‘D -- DIRECTORY RET g’. + + -- Function: magit-insert-unpulled-or-recent-commits + Insert section showing unpulled or recent commits. If an upstream + is configured for the current branch and it is ahead of the current + branch, then show the missing commits. Otherwise, show the last + ‘magit-log-section-commit-count’ commits. + + -- Function: magit-insert-recent-commits + Insert section showing the last ‘magit-log-section-commit-count’ + commits. + + -- User Option: magit-log-section-commit-count + How many recent commits ‘magit-insert-recent-commits’ and + ‘magit-insert-unpulled-or-recent-commits’ (provided there are no + unpulled commits) show. + + -- Function: magit-insert-unpulled-cherries + Insert section showing unpulled commits. Like + ‘magit-insert-unpulled-commits’ but prefix each commit that has not + been applied yet (i.e., a commit with a patch-id not shared with + any local commit) with "+", and all others with "-". + + -- Function: magit-insert-unpushed-cherries + Insert section showing unpushed commits. Like + ‘magit-insert-unpushed-commits’ but prefix each commit which has + not been applied to upstream yet (i.e., a commit with a patch-id + not shared with any upstream commit) with "+" and all others with + "-". + + See *note References Buffer:: for some more section inserters, which +could be used here. + + +File: magit.info, Node: Status Header Sections, Next: Status Module Sections, Prev: Status Sections, Up: Status Buffer + +5.1.2 Status Header Sections +---------------------------- + +The contents of status buffers is controlled using the hook +‘magit-status-sections-hook’ (see *note Status Sections::). + + By default ‘magit-insert-status-headers’ is the first member of that +hook variable. + + -- Function: magit-insert-status-headers + Insert headers sections appropriate for ‘magit-status-mode’ + buffers. The sections are inserted by running the functions on the + hook ‘magit-status-headers-hook’. + + -- User Option: magit-status-headers-hook + Hook run to insert headers sections into the status buffer. + + This hook is run by ‘magit-insert-status-headers’, which in turn + has to be a member of ‘magit-status-sections-hook’ to be used at + all. + + By default the following functions are members of the above hook: + + -- Function: magit-insert-error-header + Insert a header line showing the message about the Git error that + just occurred. + + This function is only aware of the last error that occur when Git + was run for side-effects. If, for example, an error occurs while + generating a diff, then that error won’t be inserted. Refreshing + the status buffer causes this section to disappear again. + + -- Function: magit-insert-diff-filter-header + Insert a header line showing the effective diff filters. + + -- Function: magit-insert-head-branch-header + Insert a header line about the current branch or detached ‘HEAD’. + + -- Function: magit-insert-upstream-branch-header + Insert a header line about the branch that is usually pulled into + the current branch. + + -- Function: magit-insert-push-branch-header + Insert a header line about the branch that the current branch is + usually pushed to. + + -- Function: magit-insert-tags-header + Insert a header line about the current and/or next tag, along with + the number of commits between the tag and ‘HEAD’. + + The following functions can also be added to the above hook: + + -- Function: magit-insert-repo-header + Insert a header line showing the path to the repository top-level. + + -- Function: magit-insert-remote-header + Insert a header line about the remote of the current branch. + + If no remote is configured for the current branch, then fall back + showing the "origin" remote, or if that does not exist the first + remote in alphabetic order. + + -- Function: magit-insert-user-header + Insert a header line about the current user. + + +File: magit.info, Node: Status Module Sections, Next: Status Options, Prev: Status Header Sections, Up: Status Buffer + +5.1.3 Status Module Sections +---------------------------- + +The contents of status buffers is controlled using the hook +‘magit-status-sections-hook’ (see *note Status Sections::). + + By default ‘magit-insert-modules’ is _not_ a member of that hook +variable. + + -- Function: magit-insert-modules + Insert submodule sections. + + Hook ‘magit-module-sections-hook’ controls which module sections + are inserted, and option ‘magit-module-sections-nested’ controls + whether they are wrapped in an additional section. + + -- User Option: magit-module-sections-hook + Hook run by ‘magit-insert-modules’. + + -- User Option: magit-module-sections-nested + This option controls whether ‘magit-insert-modules’ wraps inserted + sections in an additional section. + + If this is non-nil, then only a single top-level section is + inserted. If it is nil, then all sections listed in + ‘magit-module-sections-hook’ become top-level sections. + + -- Function: magit-insert-modules-overview + Insert sections for all submodules. For each section insert the + path, the branch, and the output of ‘git describe --tags’, or, + failing that, the abbreviated HEAD commit hash. + + Press ‘RET’ on such a submodule section to show its own status + buffer. Press ‘RET’ on the "Modules" section to display a list of + submodules in a separate buffer. This shows additional information + not displayed in the super-repository’s status buffer. + + -- Function: magit-insert-modules-unpulled-from-upstream + Insert sections for modules that haven’t been pulled from the + upstream yet. These sections can be expanded to show the + respective commits. + + -- Function: magit-insert-modules-unpulled-from-pushremote + Insert sections for modules that haven’t been pulled from the + push-remote yet. These sections can be expanded to show the + respective commits. + + -- Function: magit-insert-modules-unpushed-to-upstream + Insert sections for modules that haven’t been pushed to the + upstream yet. These sections can be expanded to show the + respective commits. + + -- Function: magit-insert-modules-unpushed-to-pushremote + Insert sections for modules that haven’t been pushed to the + push-remote yet. These sections can be expanded to show the + respective commits. + + +File: magit.info, Node: Status Options, Prev: Status Module Sections, Up: Status Buffer + +5.1.4 Status Options +-------------------- + + -- User Option: magit-status-margin + This option specifies whether the margin is initially shown in + Magit-Status mode buffers and how it is formatted. + + The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. + + • If INIT is non-nil, then the margin is shown initially. + • STYLE controls how to format the author or committer date. It + can be one of ‘age’ (to show the age of the commit), + ‘age-abbreviated’ (to abbreviate the time unit to a + character), or a string (suitable for ‘format-time-string’) to + show the actual date. Option + ‘magit-log-margin-show-committer-date’ controls which date is + being displayed. + • WIDTH controls the width of the margin. This exists for + forward compatibility and currently the value should not be + changed. + • AUTHOR controls whether the name of the author is also shown + by default. + • AUTHOR-WIDTH has to be an integer. When the name of the + author is shown, then this specifies how much space is used to + do so. + + Also see the proceeding section for more options concerning status +buffers. + + +File: magit.info, Node: Repository List, Next: Logging, Prev: Status Buffer, Up: Inspecting + +5.2 Repository List +=================== + + -- Command: magit-list-repositories + This command displays a list of repositories in a separate buffer. + + The option ‘magit-repository-directories’ controls which + repositories are displayed. + + -- User Option: magit-repolist-columns + This option controls what columns are displayed by the command + ‘magit-list-repositories’ and how they are displayed. + + Each element has the form ‘(HEADER WIDTH FORMAT PROPS)’. + + HEADER is the string displayed in the header. WIDTH is the width + of the column. FORMAT is a function that is called with one + argument, the repository identification (usually its basename), and + with ‘default-directory’ bound to the toplevel of its working tree. + It has to return a string to be inserted or nil. PROPS is an alist + that supports the keys ‘:right-align’, ‘:pad-right’ and ‘:sort’. + + The ‘:sort’ function has a weird interface described in the + docstring of ‘tabulated-list--get-sort’. Alternatively ‘<’ and + ‘magit-repolist-version<’ can be used as those functions are + automatically replaced with functions that satisfy the interface. + Set ‘:sort’ to ‘nil’ to inhibit sorting; if unspecified, then the + column is sortable using the default sorter. + + You may wish to display a range of numeric columns using just one + character per column and without any padding between columns, in + which case you should use an appropriate HEADER, set WIDTH to 1, + and set ‘:pad-right’ to 9. ‘+’ is substituted for numbers higher + than 9. + +The following functions can be added to the above option: + + -- Function: magit-repolist-column-ident + This function inserts the identification of the repository. + Usually this is just its basename. + + -- Function: magit-repolist-column-path + This function inserts the absolute path of the repository. + + -- Function: magit-repolist-column-version + This function inserts a description of the repository’s ‘HEAD’ + revision. + + -- Function: magit-repolist-column-branch + This function inserts the name of the current branch. + + -- Function: magit-repolist-column-upstream + This function inserts the name of the upstream branch of the + current branch. + + -- Function: magit-repolist-column-branches + This function inserts the number of branches. + + -- Function: magit-repolist-column-stashes + This function inserts the number of stashes. + + -- Function: magit-repolist-column-flag + This function inserts a flag as specified by + ‘magit-repolist-column-flag-alist’. + + By default this indicates whether there are uncommitted changes. + + • ‘N’ if there is at least one untracked file. + • ‘U’ if there is at least one unstaged file. + • ‘S’ if there is at least one staged file. + + Only the first one of these that applies is shown. + + -- Function: magit-repolist-column-flags + This functions insert all flags as specified by + ‘magit-repolist-column-flag-alist’. + + This is an alternative to function ‘magit-repolist-column-flag’, + which only lists the first one found. + + -- Function: magit-repolist-column-unpulled-from-upstream + This function inserts the number of upstream commits not in the + current branch. + + -- Function: magit-repolist-column-unpulled-from-pushremote + This function inserts the number of commits in the push branch but + not the current branch. + + -- Function: magit-repolist-column-unpushed-to-upstream + This function inserts the number of commits in the current branch + but not its upstream. + + -- Function: magit-repolist-column-unpushed-to-pushremote + This function inserts the number of commits in the current branch + but not its push branch. + +The following commands are available in repolist buffers: + +‘<RET>’ (‘magit-repolist-status’) + This command shows the status for the repository at point. + +‘m’ (‘magit-repolist-mark’) + This command marks the repository at point. + +‘u’ (‘magit-repolist-unmark’) + This command unmarks the repository at point. + +‘f’ (‘magit-repolist-fetch’) + This command fetches all marked repositories. If no repositories + are marked, then it offers to fetch all displayed repositories. + +‘5’ (‘magit-repolist-find-file-other-frame’) + This command reads a relative file-name (without completion) and + opens the respective file in each marked repository in a new frame. + If no repositories are marked, then it offers to do this for all + displayed repositories. + + +File: magit.info, Node: Logging, Next: Diffing, Prev: Repository List, Up: Inspecting + +5.3 Logging +=========== + +The status buffer contains logs for the unpushed and unpulled commits, +but that obviously isn’t enough. The transient prefix command +‘magit-log’, on ‘l’, features several suffix commands, which show a +specific log in a separate log buffer. + + Like other transient prefix commands, ‘magit-log’ also features +several infix arguments that can be changed before invoking one of the +suffix commands. However, in the case of the log transient, these +arguments may be taken from those currently in use in the current +repository’s log buffer, depending on the value of +‘magit-prefix-use-buffer-arguments’ (see *note Transient Arguments and +Buffer Variables::). + + For information about the various arguments, see *note +(gitman)git-log::. The switch ‘++order=VALUE’ is converted to one of +‘--author-date-order’, ‘--date-order’, or ‘--topo-order’ before being +passed to ‘git log’. + + The log transient also features several reflog commands. See *note +Reflog::. + +‘l’ (‘magit-log’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘l l’ (‘magit-log-current’) + Show log for the current branch. When ‘HEAD’ is detached or with a + prefix argument, show log for one or more revs read from the + minibuffer. + +‘l h’ (‘magit-log-head’) + Show log for ‘HEAD’. + +‘l u’ (‘magit-log-related’) + Show log for the current branch, its upstream and its push target. + When the upstream is a local branch, then also show its own + upstream. When ‘HEAD’ is detached, then show log for that, the + previously checked out branch and its upstream and push-target. + +‘l o’ (‘magit-log-other’) + Show log for one or more revs read from the minibuffer. The user + can input any revision or revisions separated by a space, or even + ranges, but only branches, tags, and a representation of the commit + at point are available as completion candidates. + +‘l L’ (‘magit-log-branches’) + Show log for all local branches and ‘HEAD’. + +‘l b’ (‘magit-log-all-branches’) + Show log for all local and remote branches and ‘HEAD’. + +‘l a’ (‘magit-log-all’) + Show log for all references and ‘HEAD’. + + Two additional commands that show the log for the file or blob that +is being visited in the current buffer exists, see *note Commands for +Buffers Visiting Files::. The command ‘magit-cherry’ also shows a log, +see *note Cherries::. + +* Menu: + +* Refreshing Logs:: +* Log Buffer:: +* Log Margin:: +* Select from Log:: +* Reflog:: +* Cherries:: + + +File: magit.info, Node: Refreshing Logs, Next: Log Buffer, Up: Logging + +5.3.1 Refreshing Logs +--------------------- + +The transient prefix command ‘magit-log-refresh’, on ‘L’, can be used to +change the log arguments used in the current buffer, without changing +which log is shown. This works in dedicated log buffers, but also in +the status buffer. + +‘L’ (‘magit-log-refresh’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘L g’ (‘magit-log-refresh’) + This suffix command sets the local log arguments for the current + buffer. + +‘L s’ (‘magit-log-set-default-arguments’) + This suffix command sets the default log arguments for buffers of + the same type as that of the current buffer. Other existing + buffers of the same type are not affected because their local + values have already been initialized. + +‘L w’ (‘magit-log-save-default-arguments’) + This suffix command sets the default log arguments for buffers of + the same type as that of the current buffer, and saves the value + for future sessions. Other existing buffers of the same type are + not affected because their local values have already been + initialized. + +‘L L’ (‘magit-toggle-margin’) + Show or hide the margin. + + +File: magit.info, Node: Log Buffer, Next: Log Margin, Prev: Refreshing Logs, Up: Logging + +5.3.2 Log Buffer +---------------- + +‘L’ (‘magit-log-refresh’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + See *note Refreshing Logs::. + +‘q’ (‘magit-log-bury-buffer’) + Bury the current buffer or the revision buffer in the same frame. + Like ‘magit-mode-bury-buffer’ (which see) but with a negative + prefix argument instead bury the revision buffer, provided it is + displayed in the current frame. + +‘C-c C-b’ (‘magit-go-backward’) + Move backward in current buffer’s history. + +‘C-c C-f’ (‘magit-go-forward’) + Move forward in current buffer’s history. + +‘C-c C-n’ (‘magit-log-move-to-parent’) + Move to a parent of the current commit. By default, this is the + first parent, but a numeric prefix can be used to specify another + parent. + +‘j’ (‘magit-log-move-to-revision’) + Read a revision and move to it in current log buffer. + + If the chosen reference or revision isn’t being displayed in the + current log buffer, then inform the user about that and do nothing + else. + + If invoked outside any log buffer, then display the log buffer of + the current repository first; creating it if necessary. + +‘<SPC>’ (‘magit-diff-show-or-scroll-up’) + Update the commit or diff buffer for the thing at point. + + Either show the commit or stash at point in the appropriate buffer, + or if that buffer is already being displayed in the current frame + and contains information about that commit or stash, then instead + scroll the buffer up. If there is no commit or stash at point, + then prompt for a commit. + +‘<DEL>’ (‘magit-diff-show-or-scroll-down’) + Update the commit or diff buffer for the thing at point. + + Either show the commit or stash at point in the appropriate buffer, + or if that buffer is already being displayed in the current frame + and contains information about that commit or stash, then instead + scroll the buffer down. If there is no commit or stash at point, + then prompt for a commit. + +‘=’ (‘magit-log-toggle-commit-limit’) + Toggle the number of commits the current log buffer is limited to. + If the number of commits is currently limited, then remove that + limit. Otherwise set it to 256. + +‘+’ (‘magit-log-double-commit-limit’) + Double the number of commits the current log buffer is limited to. + +‘-’ (‘magit-log-half-commit-limit’) + Half the number of commits the current log buffer is limited to. + + -- User Option: magit-log-auto-more + Insert more log entries automatically when moving past the last + entry. Only considered when moving past the last entry with + ‘magit-goto-*-section’ commands. + + -- User Option: magit-log-show-refname-after-summary + Whether to show the refnames after the commit summaries. This is + useful if you use really long branch names. + + -- User Option: magit-log-show-color-graph-limit + When showing more commits than specified by this option, then the + ‘--color’ argument, if specified, is silently dropped. This is + necessary because the ‘ansi-color’ library, which is used to turn + control sequences into faces, is just too slow. + + -- User Option: magit-log-show-signatures-limit + When showing more commits than specified by this option, then the + ‘--show-signature’ argument, if specified, is silently dropped. + This is necessary because checking the signature of a large number + of commits is just too slow. + + Magit displays references in logs a bit differently from how Git does +it. + + Local branches are blue and remote branches are green. Of course +that depends on the used theme, as do the colors used for other types of +references. The current branch has a box around it, as do remote +branches that are their respective remote’s ‘HEAD’ branch. + + If a local branch and its push-target point at the same commit, then +their names are combined to preserve space and to make that relationship +visible. For example: + + origin/feature + [green][blue-] + + instead of + + feature origin/feature + [blue-] [green-------] + + Also note that while the transient features the ‘--show-signature’ +argument, that won’t actually be used when enabled, because Magit +defaults to use just one line per commit. Instead the commit colorized +to indicate the validity of the signed commit object, using the faces +named ‘magit-signature-*’ (which see). + + For a description of ‘magit-log-margin’ see *note Log Margin::. + + +File: magit.info, Node: Log Margin, Next: Select from Log, Prev: Log Buffer, Up: Logging + +5.3.3 Log Margin +---------------- + +In buffers which show one or more logs, it is possible to show +additional information about each commit in the margin. The options +used to configure the margin are named ‘magit-INFIX-margin’, where INFIX +is the same as in the respective major-mode ‘magit-INFIX-mode’. In +regular log buffers that would be ‘magit-log-margin’. + + -- User Option: magit-log-margin + This option specifies whether the margin is initially shown in + Magit-Log mode buffers and how it is formatted. + + The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. + + • If INIT is non-nil, then the margin is shown initially. + • STYLE controls how to format the author or committer date. It + can be one of ‘age’ (to show the age of the commit), + ‘age-abbreviated’ (to abbreviate the time unit to a + character), or a string (suitable for ‘format-time-string’) to + show the actual date. Option + ‘magit-log-margin-show-committer-date’ controls which date is + being displayed. + • WIDTH controls the width of the margin. This exists for + forward compatibility and currently the value should not be + changed. + • AUTHOR controls whether the name of the author is also shown + by default. + • AUTHOR-WIDTH has to be an integer. When the name of the + author is shown, then this specifies how much space is used to + do so. + + You can change the STYLE and AUTHOR-WIDTH of all ‘magit-INFIX-margin’ +options to the same values by customizing ‘magit-log-margin’ *before* +‘magit’ is loaded. If you do that, then the respective values for the +other options will default to what you have set for that variable. +Likewise if you set INIT in ‘magit-log-margin’ to ‘nil’, then that is +used in the default of all other options. But setting it to ‘t’, i.e. +re-enforcing the default for that option, does not carry to other +options. + + -- User Option: magit-log-margin-show-committer-date + This option specifies whether to show the committer date in the + margin. This option only controls whether the committer date is + displayed instead of the author date. Whether some date is + displayed in the margin and whether the margin is displayed at all + is controlled by other options. + +‘L’ (‘magit-margin-settings’) + This transient prefix command binds the following suffix commands, + each of which changes the appearance of the margin in some way. + + In some buffers that support the margin, ‘L’ is instead bound to +‘magit-log-refresh’, but that transient features the same commands, and +then some other unrelated commands. + +‘L L’ (‘magit-toggle-margin’) + This command shows or hides the margin. + +‘L l’ (‘magit-cycle-margin-style’) + This command cycles the style used for the margin. + +‘L d’ (‘magit-toggle-margin-details’) + This command shows or hides details in the margin. + + +File: magit.info, Node: Select from Log, Next: Reflog, Prev: Log Margin, Up: Logging + +5.3.4 Select from Log +--------------------- + +When the user has to select a recent commit that is reachable from +‘HEAD’, using regular completion would be inconvenient (because most +humans cannot remember hashes or "HEAD~5", at least not without double +checking). Instead a log buffer is used to select the commit, which has +the advantage that commits are presented in order and with the commit +message. + + Such selection logs are used when selecting the beginning of a rebase +and when selecting the commit to be squashed into. + + In addition to the key bindings available in all log buffers, the +following additional key bindings are available in selection log +buffers: + +‘C-c C-c’ (‘magit-log-select-pick’) + Select the commit at point and act on it. Call + ‘magit-log-select-pick-function’ with the selected commit as + argument. + +‘C-c C-k’ (‘magit-log-select-quit’) + Abort selecting a commit, don’t act on any commit. + + -- User Option: magit-log-select-margin + This option specifies whether the margin is initially shown in + Magit-Log-Select mode buffers and how it is formatted. + + The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. + + • If INIT is non-nil, then the margin is shown initially. + • STYLE controls how to format the author or committer date. It + can be one of ‘age’ (to show the age of the commit), + ‘age-abbreviated’ (to abbreviate the time unit to a + character), or a string (suitable for ‘format-time-string’) to + show the actual date. Option + ‘magit-log-margin-show-committer-date’ controls which date is + being displayed. + • WIDTH controls the width of the margin. This exists for + forward compatibility and currently the value should not be + changed. + • AUTHOR controls whether the name of the author is also shown + by default. + • AUTHOR-WIDTH has to be an integer. When the name of the + author is shown, then this specifies how much space is used to + do so. + + +File: magit.info, Node: Reflog, Next: Cherries, Prev: Select from Log, Up: Logging + +5.3.5 Reflog +------------ + +Also see *note (gitman)git-reflog::. + + These reflog commands are available from the log transient. See +*note Logging::. + +‘l r’ (‘magit-reflog-current’) + Display the reflog of the current branch. + +‘l O’ (‘magit-reflog-other’) + Display the reflog of a branch or another ref. + +‘l H’ (‘magit-reflog-head’) + Display the ‘HEAD’ reflog. + + -- User Option: magit-reflog-margin + This option specifies whether the margin is initially shown in + Magit-Reflog mode buffers and how it is formatted. + + The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. + + • If INIT is non-nil, then the margin is shown initially. + • STYLE controls how to format the author or committer date. It + can be one of ‘age’ (to show the age of the commit), + ‘age-abbreviated’ (to abbreviate the time unit to a + character), or a string (suitable for ‘format-time-string’) to + show the actual date. Option + ‘magit-log-margin-show-committer-date’ controls which date is + being displayed. + • WIDTH controls the width of the margin. This exists for + forward compatibility and currently the value should not be + changed. + • AUTHOR controls whether the name of the author is also shown + by default. + • AUTHOR-WIDTH has to be an integer. When the name of the + author is shown, then this specifies how much space is used to + do so. + + +File: magit.info, Node: Cherries, Prev: Reflog, Up: Logging + +5.3.6 Cherries +-------------- + +Cherries are commits that haven’t been applied upstream (yet), and are +usually visualized using a log. Each commit is prefixed with ‘-’ if it +has an equivalent in the upstream and ‘+’ if it does not, i.e., if it is +a cherry. + + The command ‘magit-cherry’ shows cherries for a single branch, but +the references buffer (see *note References Buffer::) can show cherries +for multiple "upstreams" at once. + + Also see *note (gitman)git-reflog::. + +‘Y’ (‘magit-cherry’) + Show commits that are in a certain branch but that have not been + merged in the upstream branch. + + -- User Option: magit-cherry-margin + This option specifies whether the margin is initially shown in + Magit-Cherry mode buffers and how it is formatted. + + The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. + + • If INIT is non-nil, then the margin is shown initially. + • STYLE controls how to format the author or committer date. It + can be one of ‘age’ (to show the age of the commit), + ‘age-abbreviated’ (to abbreviate the time unit to a + character), or a string (suitable for ‘format-time-string’) to + show the actual date. Option + ‘magit-log-margin-show-committer-date’ controls which date is + being displayed. + • WIDTH controls the width of the margin. This exists for + forward compatibility and currently the value should not be + changed. + • AUTHOR controls whether the name of the author is also shown + by default. + • AUTHOR-WIDTH has to be an integer. When the name of the + author is shown, then this specifies how much space is used to + do so. + + +File: magit.info, Node: Diffing, Next: Ediffing, Prev: Logging, Up: Inspecting + +5.4 Diffing +=========== + +The status buffer contains diffs for the staged and unstaged commits, +but that obviously isn’t enough. The transient prefix command +‘magit-diff’, on ‘d’, features several suffix commands, which show a +specific diff in a separate diff buffer. + + Like other transient prefix commands, ‘magit-diff’ also features +several infix arguments that can be changed before invoking one of the +suffix commands. However, in the case of the diff transient, these +arguments may be taken from those currently in use in the current +repository’s diff buffer, depending on the value of +‘magit-prefix-use-buffer-arguments’ (see *note Transient Arguments and +Buffer Variables::). + + Also see *note (gitman)git-diff::. + +‘d’ (‘magit-diff’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘d d’ (‘magit-diff-dwim’) + Show changes for the thing at point. + +‘d r’ (‘magit-diff-range’) + Show differences between two commits. + + RANGE should be a range (A..B or A...B) but can also be a single + commit. If one side of the range is omitted, then it defaults to + ‘HEAD’. If just a commit is given, then changes in the working + tree relative to that commit are shown. + + If the region is active, use the revisions on the first and last + line of the region. With a prefix argument, instead of diffing the + revisions, choose a revision to view changes along, starting at the + common ancestor of both revisions (i.e., use a "..." range). + +‘d w’ (‘magit-diff-working-tree’) + Show changes between the current working tree and the ‘HEAD’ + commit. With a prefix argument show changes between the working + tree and a commit read from the minibuffer. + +‘d s’ (‘magit-diff-staged’) + Show changes between the index and the ‘HEAD’ commit. With a + prefix argument show changes between the index and a commit read + from the minibuffer. + +‘d u’ (‘magit-diff-unstaged’) + Show changes between the working tree and the index. + +‘d p’ (‘magit-diff-paths’) + Show changes between any two files on disk. + + All of the above suffix commands update the repository’s diff buffer. +The diff transient also features two commands which show differences in +another buffer: + +‘d c’ (‘magit-show-commit’) + Show the commit at point. If there is no commit at point or with a + prefix argument, prompt for a commit. + +‘d t’ (‘magit-stash-show’) + Show all diffs of a stash in a buffer. + + Two additional commands that show the diff for the file or blob that +is being visited in the current buffer exists, see *note Commands for +Buffers Visiting Files::. + +* Menu: + +* Refreshing Diffs:: +* Commands Available in Diffs:: +* Diff Options:: +* Revision Buffer:: + + +File: magit.info, Node: Refreshing Diffs, Next: Commands Available in Diffs, Up: Diffing + +5.4.1 Refreshing Diffs +---------------------- + +The transient prefix command ‘magit-diff-refresh’, on ‘D’, can be used +to change the diff arguments used in the current buffer, without +changing which diff is shown. This works in dedicated diff buffers, but +also in the status buffer. + + (There is one exception; diff arguments cannot be changed in buffers +created by ‘magit-merge-preview’ because the underlying Git command does +not support these arguments.) + +‘D’ (‘magit-diff-refresh’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘D g’ (‘magit-diff-refresh’) + This suffix command sets the local diff arguments for the current + buffer. + +‘D s’ (‘magit-diff-set-default-arguments’) + This suffix command sets the default diff arguments for buffers of + the same type as that of the current buffer. Other existing + buffers of the same type are not affected because their local + values have already been initialized. + +‘D w’ (‘magit-diff-save-default-arguments’) + This suffix command sets the default diff arguments for buffers of + the same type as that of the current buffer, and saves the value + for future sessions. Other existing buffers of the same type are + not affected because their local values have already been + initialized. + +‘D t’ (‘magit-diff-toggle-refine-hunk’) + This command toggles hunk refinement on or off. + +‘D r’ (‘magit-diff-switch-range-type’) + This command converts the diff range type from "revA..revB" to + "revB...revA", or vice versa. + +‘D f’ (‘magit-diff-flip-revs’) + This command swaps revisions in the diff range from "revA..revB" to + "revB..revA", or vice versa. + +‘D F’ (‘magit-diff-toggle-file-filter’) + This command toggles the file restriction of the diffs in the + current buffer, allowing you to quickly switch between viewing all + the changes in the commit and the restricted subset. As a special + case, when this command is called from a log buffer, it toggles the + file restriction in the repository’s revision buffer, which is + useful when you display a revision from a log buffer that is + restricted to a file or files. + + In addition to the above transient, which allows changing any of the +supported arguments, there also exist some commands that change only a +particular argument. + +‘-’ (‘magit-diff-less-context’) + This command decreases the context for diff hunks by COUNT lines. + +‘+’ (‘magit-diff-more-context’) + This command increases the context for diff hunks by COUNT lines. + +‘0’ (‘magit-diff-default-context’) + This command resets the context for diff hunks to the default + height. + + The following commands quickly change what diff is being displayed +without having to using one of the diff transient. + +‘C-c C-d’ (‘magit-diff-while-committing’) + While committing, this command shows the changes that are about to + be committed. While amending, invoking the command again toggles + between showing just the new changes or all the changes that will + be committed. + + This binding is available in the diff buffer as well as the commit + message buffer. + +‘C-c C-b’ (‘magit-go-backward’) + This command moves backward in current buffer’s history. + +‘C-c C-f’ (‘magit-go-forward’) + This command moves forward in current buffer’s history. + + +File: magit.info, Node: Commands Available in Diffs, Next: Diff Options, Prev: Refreshing Diffs, Up: Diffing + +5.4.2 Commands Available in Diffs +--------------------------------- + +Some commands are only available if point is inside a diff. + + ‘magit-diff-visit-file’ and related commands visit the appropriate +version of the file that the diff at point is about. Likewise +‘magit-diff-visit-worktree-file’ and related commands visit the worktree +version of the file that the diff at point is about. See *note Visiting +Files and Blobs from a Diff:: for more information and the key bindings. + +‘C-c C-t’ (‘magit-diff-trace-definition’) + This command shows a log for the definition at point. + + -- User Option: magit-log-trace-definition-function + The function specified by this option is used by + ‘magit-log-trace-definition’ to determine the function at point. + For major-modes that have special needs, you could set the local + value using the mode’s hook. + +‘C-c C-e’ (‘magit-diff-edit-hunk-commit’) + From a hunk, this command edits the respective commit and visits + the file. + + First it visits the file being modified by the hunk at the correct + location using ‘magit-diff-visit-file’. This actually visits a + blob. When point is on a diff header, not within an individual + hunk, then this visits the blob the first hunk is about. + + Then it invokes ‘magit-edit-line-commit’, which uses an interactive + rebase to make the commit editable, or if that is not possible + because the commit is not reachable from ‘HEAD’ by checking out + that commit directly. This also causes the actual worktree file to + be visited. + + Neither the blob nor the file buffer are killed when finishing the + rebase. If that is undesirable, then it might be better to use + ‘magit-rebase-edit-commit’ instead of this command. + +‘j’ (‘magit-jump-to-diffstat-or-diff’) + This command jumps to the diffstat or diff. When point is on a + file inside the diffstat section, then jump to the respective diff + section. Otherwise, jump to the diffstat section or a child + thereof. + + The next two commands are not specific to Magit-Diff mode (or and +Magit buffer for that matter), but it might be worth pointing out that +they are available here too. + +‘<SPC>’ (‘scroll-up’) + This command scrolls text upward. + +‘<DEL>’ (‘scroll-down’) + This command scrolls text downward. + + +File: magit.info, Node: Diff Options, Next: Revision Buffer, Prev: Commands Available in Diffs, Up: Diffing + +5.4.3 Diff Options +------------------ + + -- User Option: magit-diff-refine-hunk + Whether to show word-granularity differences within diff hunks. + + • ‘nil’ Never show fine differences. + • ‘t’ Show fine differences for the current diff hunk only. + • ‘all’ Show fine differences for all displayed diff hunks. + + -- User Option: magit-diff-refine-ignore-whitespace + Whether to ignore whitespace changes in word-granularity + differences. + + -- User Option: magit-diff-adjust-tab-width + Whether to adjust the width of tabs in diffs. + + Determining the correct width can be expensive if it requires + opening large and/or many files, so the widths are cached in the + variable ‘magit-diff--tab-width-cache’. Set that to nil to + invalidate the cache. + + • ‘nil’ Never adjust tab width. Use ‘tab-width’s value from the + Magit buffer itself instead. + + • ‘t’ If the corresponding file-visiting buffer exits, then use + ‘tab-width’’s value from that buffer. Doing this is cheap, so + this value is used even if a corresponding cache entry exists. + + • ‘always’ If there is no such buffer, then temporarily visit + the file to determine the value. + + • NUMBER Like ‘always’, but don’t visit files larger than NUMBER + bytes. + + -- User Option: magit-diff-paint-whitespace + Specify where to highlight whitespace errors. + + See ‘magit-diff-highlight-trailing’, + ‘magit-diff-highlight-indentation’. The symbol ‘t’ means in all + diffs, ‘status’ means only in the status buffer, and nil means + nowhere. + + • ‘nil’ Never highlight whitespace errors. + • ‘t’ Highlight whitespace errors everywhere. + • ‘uncommitted’ Only highlight whitespace errors in diffs + showing uncommitted changes. For backward compatibility + ‘status’ is treated as a synonym. + + -- User Option: magit-diff-paint-whitespace-lines + Specify in what kind of lines to highlight whitespace errors. + + • ‘t’ Highlight only in added lines. + • ‘both’ Highlight in added and removed lines. + • ‘all’ Highlight in added, removed and context lines. + + -- User Option: magit-diff-highlight-trailing + Whether to highlight whitespace at the end of a line in diffs. + Used only when ‘magit-diff-paint-whitespace’ is non-nil. + + -- User Option: magit-diff-highlight-indentation + This option controls whether to highlight the indentation in case + it used the "wrong" indentation style. Indentation is only + highlighted if ‘magit-diff-paint-whitespace’ is also non-nil. + + The value is an alist of the form ‘((REGEXP . INDENT)...)’. The + path to the current repository is matched against each element in + reverse order. Therefore if a REGEXP matches, then earlier + elements are not tried. + + If the used INDENT is ‘tabs’, highlight indentation with tabs. If + INDENT is an integer, highlight indentation with at least that many + spaces. Otherwise, highlight neither. + + -- User Option: magit-diff-hide-trailing-cr-characters + Whether to hide ^M characters at the end of a line in diffs. + + -- User Option: magit-diff-highlight-hunk-region-functions + This option specifies the functions used to highlight the + hunk-internal region. + + ‘magit-diff-highlight-hunk-region-dim-outside’ overlays the outside + of the hunk internal selection with a face that causes the added + and removed lines to have the same background color as context + lines. This function should not be removed from the value of this + option. + + ‘magit-diff-highlight-hunk-region-using-overlays’ and + ‘magit-diff-highlight-hunk-region-using-underline’ emphasize the + region by placing delimiting horizontal lines before and after it. + Both of these functions have glitches which cannot be fixed due to + limitations of Emacs’ display engine. For more information see + <https://github.com/magit/magit/issues/2758> ff. + + Instead of, or in addition to, using delimiting horizontal lines, + to emphasize the boundaries, you may wish to emphasize the text + itself, using ‘magit-diff-highlight-hunk-region-using-face’. + + In terminal frames it’s not possible to draw lines as the overlay + and underline variants normally do, so there they fall back to + calling the face function instead. + + -- User Option: magit-diff-unmarked-lines-keep-foreground + This option controls whether added and removed lines outside the + hunk-internal region only lose their distinct background color or + also the foreground color. Whether the outside of the region is + dimmed at all depends on + ‘magit-diff-highlight-hunk-region-functions’. + + -- User Option: magit-diff-extra-stat-arguments + This option specifies additional arguments to be used alongside + ‘--stat’. + + The value is a list of zero or more arguments or a function that + takes no argument and returns such a list. These arguments are + allowed here: ‘--stat-width’, ‘--stat-name-width’, + ‘--stat-graph-width’ and ‘--compact-summary’. Also see *note + (gitman)git-diff::. + + +File: magit.info, Node: Revision Buffer, Prev: Diff Options, Up: Diffing + +5.4.4 Revision Buffer +--------------------- + + -- User Option: magit-revision-insert-related-refs + Whether to show related branches in revision buffers. + + • ‘nil’ Don’t show any related branches. + • ‘t’ Show related local branches. + • ‘all’ Show related local and remote branches. + • ‘mixed’ Show all containing branches and local merged + branches. + + -- User Option: magit-revision-show-gravatars + Whether to show gravatar images in revision buffers. + + If ‘nil’, then don’t insert any gravatar images. If ‘t’, then + insert both images. If ‘author’ or ‘committer’, then insert only + the respective image. + + If you have customized the option ‘magit-revision-headers-format’ + and want to insert the images then you might also have to specify + where to do so. In that case the value has to be a cons-cell of + two regular expressions. The car specifies where to insert the + author’s image. The top half of the image is inserted right after + the matched text, the bottom half on the next line in the same + column. The cdr specifies where to insert the committer’s image, + accordingly. Either the car or the cdr may be nil." + + -- User Option: magit-revision-use-hash-sections + Whether to turn hashes inside the commit message into sections. + + If non-nil, then hashes inside the commit message are turned into + ‘commit’ sections. There is a trade off to be made between + performance and reliability: + + • ‘slow’ calls git for every word to be absolutely sure. + • ‘quick’ skips words less than seven characters long. + • ‘quicker’ additionally skips words that don’t contain a + number. + • ‘quickest’ uses all words that are at least seven characters + long and which contain at least one number as well as at least + one letter. + + If nil, then no hashes are turned into sections, but you can still + visit the commit at point using "RET". + + The diffs shown in the revision buffer may be automatically +restricted to a subset of the changed files. If the revision buffer is +displayed from a log buffer, the revision buffer will share the same +file restriction as that log buffer (also see the command +‘magit-diff-toggle-file-filter’). + + -- User Option: magit-revision-filter-files-on-follow + Whether showing a commit from a log buffer honors the log’s file + filter when the log arguments include ‘--follow’. + + When this option is nil, displaying a commit from a log ignores the + log’s file filter if the log arguments include ‘--follow’. Doing + so avoids showing an empty diff in revision buffers for commits + before a rename event. In such cases, the ‘--patch’ argument of + the log transient can be used to show the file-restricted diffs + inline. + + Set this option to non-nil to keep the log’s file restriction even + if ‘--follow’ is present in the log arguments. + + If the revision buffer is not displayed from a log buffer, the file +restriction is determined as usual (see *note Transient Arguments and +Buffer Variables::). + + +File: magit.info, Node: Ediffing, Next: References Buffer, Prev: Diffing, Up: Inspecting + +5.5 Ediffing +============ + +This section describes how to enter Ediff from Magit buffers. For +information on how to use Ediff itself, see *note (ediff)Top::. + +‘e’ (‘magit-ediff-dwim’) + Compare, stage, or resolve using Ediff. + + This command tries to guess what file, and what commit or range the + user wants to compare, stage, or resolve using Ediff. It might + only be able to guess either the file, or range/commit, in which + case the user is asked about the other. It might not always guess + right, in which case the appropriate ‘magit-ediff-*’ command has to + be used explicitly. If it cannot read the user’s mind at all, then + it asks the user for a command to run. + +‘E’ (‘magit-ediff’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + +‘E r’ (‘magit-ediff-compare’) + Compare two revisions of a file using Ediff. + + If the region is active, use the revisions on the first and last + line of the region. With a prefix argument, instead of diffing the + revisions, choose a revision to view changes along, starting at the + common ancestor of both revisions (i.e., use a "..." range). + +‘E m’ (‘magit-ediff-resolve-rest’) + This command allows you to resolve outstanding conflicts in the + file at point using Ediff. If there is no file at point or if it + doesn’t have any unmerged changes, then this command prompts for a + file. + + Provided that the value of ‘merge.conflictstyle’ is ‘diff3’, you + can view the file’s merge-base revision using ‘/’ in the Ediff + control buffer. + + The A, B and Ancestor buffers are constructed from the conflict + markers in the worktree file. Because you and/or Git may have + already resolved some conflicts, that means that these buffers may + not contain the actual versions from the respective blobs. + +‘E M’ (‘magit-ediff-resolve-all’) + This command allows you to resolve all conflicts in the file at + point using Ediff. If there is no file at point or if it doesn’t + have any unmerged changes, then this command prompts for a file. + + Provided that the value of ‘merge.conflictstyle’ is ‘diff3’, you + can view the file’s merge-base revision using ‘/’ in the Ediff + control buffer. + + First the file in the worktree is moved aside, appending the suffix + ‘.ORIG’, so that you could later go back to that version. Then it + is reconstructed from the two sides of the conflict and the + merge-base, if available. + + It would be nice if the worktree file were just used as-is, but + Ediff does not support that. This means that all conflicts, that + Git has already resolved, are restored. On the other hand Ediff + also tries to resolve conflicts, and in many cases Ediff and Git + should produce similar results. + + However if you have already resolved some conflicts manually, then + those changes are discarded (though you can recover them from the + backup file). In such cases ‘magit-ediff-resolve-rest’ might be + more suitable. + + The advantage that this command has over ‘magit-ediff-resolve-rest’ + is that the A, B and Ancestor buffers correspond to blobs from the + respective commits, allowing you to inspect a side in context and + to use Magit commands in these buffers to do so. Blame and log + commands are particularly useful here. + +‘E t’ (‘magit-git-mergetool’) + This command does not actually use Ediff. While it serves the same + purpose as ‘magit-ediff-resolve-rest’, it uses ‘git mergetool + --gui’ to resolve conflicts. + + With a prefix argument this acts as a transient prefix command, + allowing the user to select the mergetool and change some settings. + +‘E s’ (‘magit-ediff-stage’) + Stage and unstage changes to a file using Ediff, defaulting to the + file at point. + +‘E u’ (‘magit-ediff-show-unstaged’) + Show unstaged changes to a file using Ediff. + +‘E i’ (‘magit-ediff-show-staged’) + Show staged changes to a file using Ediff. + +‘E w’ (‘magit-ediff-show-working-tree’) + Show changes in a file between ‘HEAD’ and working tree using Ediff. + +‘E c’ (‘magit-ediff-show-commit’) + Show changes to a file introduced by a commit using Ediff. + +‘E z’ (‘magit-ediff-show-stash’) + Show changes to a file introduced by a stash using Ediff. + + -- User Option: magit-ediff-dwim-resolve-function + This option controls which function ‘magit-ediff-dwim’ uses to + resolve conflicts. One of ‘magit-ediff-resolve-rest’, + ‘magit-ediff-resolve-all’ or ‘magit-git-mergetool’; which are all + discussed above. + + -- User Option: magit-ediff-dwim-show-on-hunks + This option controls what command ‘magit-ediff-dwim’ calls when + point is on uncommitted hunks. When nil, always run + ‘magit-ediff-stage’. Otherwise, use ‘magit-ediff-show-staged’ and + ‘magit-ediff-show-unstaged’ to show staged and unstaged changes, + respectively. + + -- User Option: magit-ediff-show-stash-with-index + This option controls whether ‘magit-ediff-show-stash’ includes a + buffer containing the file’s state in the index at the time the + stash was created. This makes it possible to tell which changes in + the stash were staged. + + -- User Option: magit-ediff-quit-hook + This hook is run after quitting an Ediff session that was created + using a Magit command. The hook functions are run inside the Ediff + control buffer, and should not change the current buffer. + + This is similar to ‘ediff-quit-hook’ but takes the needs of Magit + into account. The regular ‘ediff-quit-hook’ is ignored by Ediff + sessions that were created using a Magit command. + + +File: magit.info, Node: References Buffer, Next: Bisecting, Prev: Ediffing, Up: Inspecting + +5.6 References Buffer +===================== + +‘y’ (‘magit-show-refs’) + This command lists branches and tags in a dedicated buffer. + + However if this command is invoked again from this buffer or if it + is invoked with a prefix argument, then it acts as a transient + prefix command, which binds the following suffix commands and some + infix arguments. + + All of the following suffix commands list exactly the same branches +and tags. The only difference the optional feature that can be enabled +by changing the value of ‘magit-refs-show-commit-count’ (see below). +These commands specify a different branch or commit against which all +the other references are compared. + +‘y y’ (‘magit-show-refs-head’) + This command lists branches and tags in a dedicated buffer. Each + reference is being compared with ‘HEAD’. + +‘y c’ (‘magit-show-refs-current’) + This command lists branches and tags in a dedicated buffer. Each + reference is being compared with the current branch or ‘HEAD’ if it + is detached. + +‘y o’ (‘magit-show-refs-other’) + This command lists branches and tags in a dedicated buffer. Each + reference is being compared with a branch read from the user. + +‘y r’ (‘magit-refs-set-show-commit-count’) + This command changes for which refs the commit count is shown. + + -- User Option: magit-refs-show-commit-count + Whether to show commit counts in Magit-Refs mode buffers. + + • ‘all’ Show counts for branches and tags. + • ‘branch’ Show counts for branches only. + • ‘nil’ Never show counts. + + The default is ‘nil’ because anything else can be very expensive. + + -- User Option: magit-refs-pad-commit-counts + Whether to pad all commit counts on all sides in Magit-Refs mode + buffers. + + If this is nil, then some commit counts are displayed right next to + one of the branches that appear next to the count, without any + space in between. This might look bad if the branch name faces + look too similar to ‘magit-dimmed’. + + If this is non-nil, then spaces are placed on both sides of all + commit counts. + + -- User Option: magit-refs-show-remote-prefix + Whether to show the remote prefix in lists of remote branches. + + Showing the prefix is redundant because the name of the remote is + already shown in the heading preceding the list of its branches. + + -- User Option: magit-refs-primary-column-width + Width of the primary column in ‘magit-refs-mode’ buffers. The + primary column is the column that contains the name of the branch + that the current row is about. + + If this is an integer, then the column is that many columns wide. + Otherwise it has to be a cons-cell of two integers. The first + specifies the minimal width, the second the maximal width. In that + case the actual width is determined using the length of the names + of the shown local branches. (Remote branches and tags are not + taken into account when calculating to optimal width.) + + -- User Option: magit-refs-focus-column-width + Width of the focus column in ‘magit-refs-mode’ buffers. + + The focus column is the first column, which marks one branch + (usually the current branch) as the focused branch using ‘*’ or + ‘@’. For each other reference, this column optionally shows how + many commits it is ahead of the focused branch and ‘<’, or if it + isn’t ahead then the commits it is behind and ‘>’, or if it isn’t + behind either, then a ‘=’. + + This column may also display only ‘*’ or ‘@’ for the focused + branch, in which case this option is ignored. Use ‘L v’ to change + the verbosity of this column. + + -- User Option: magit-refs-margin + This option specifies whether the margin is initially shown in + Magit-Refs mode buffers and how it is formatted. + + The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. + + • If INIT is non-nil, then the margin is shown initially. + • STYLE controls how to format the author or committer date. It + can be one of ‘age’ (to show the age of the commit), + ‘age-abbreviated’ (to abbreviate the time unit to a + character), or a string (suitable for ‘format-time-string’) to + show the actual date. Option + ‘magit-log-margin-show-committer-date’ controls which date is + being displayed. + • WIDTH controls the width of the margin. This exists for + forward compatibility and currently the value should not be + changed. + • AUTHOR controls whether the name of the author is also shown + by default. + • AUTHOR-WIDTH has to be an integer. When the name of the + author is shown, then this specifies how much space is used to + do so. + + -- User Option: magit-refs-margin-for-tags + This option specifies whether to show information about tags in the + margin. This is disabled by default because it is slow if there + are many tags. + + The following variables control how individual refs are displayed. +If you change one of these variables (especially the "%c" part), then +you should also change the others to keep things aligned. The following +%-sequences are supported: + + • ‘%a’ Number of commits this ref has over the one we compare to. + • ‘%b’ Number of commits the ref we compare to has over this one. + • ‘%c’ Number of commits this ref has over the one we compare to. + For the ref which all other refs are compared this is instead "@", + if it is the current branch, or "#" otherwise. + • ‘%C’ For the ref which all other refs are compared this is "@", if + it is the current branch, or "#" otherwise. For all other refs " + ". + • ‘%h’ Hash of this ref’s tip. + • ‘%m’ Commit summary of the tip of this ref. + • ‘%n’ Name of this ref. + • ‘%u’ Upstream of this local branch. + • ‘%U’ Upstream of this local branch and additional local vs. + upstream information. + + -- User Option: magit-refs-filter-alist + The purpose of this option is to forgo displaying certain refs + based on their name. If you want to not display any refs of a + certain type, then you should remove the appropriate function from + ‘magit-refs-sections-hook’ instead. + + This alist controls which tags and branches are omitted from being + displayed in ‘magit-refs-mode’ buffers. If it is ‘nil’, then all + refs are displayed (subject to ‘magit-refs-sections-hook’). + + All keys are tried in order until one matches. Then its value is + used and subsequent elements are ignored. If the value is non-nil, + then the reference is displayed, otherwise it is not. If no + element matches, then the reference is displayed. + + A key can either be a regular expression that the refname has to + match, or a function that takes the refname as only argument and + returns a boolean. A remote branch such as "origin/master" is + displayed as just "master", however for this comparison the former + is used. + +‘<RET>’ (‘magit-visit-ref’) + This command visits the reference or revision at point in another + buffer. If there is no revision at point or with a prefix argument + then it prompts for a revision. + + This command behaves just like ‘magit-show-commit’ as described + above, except if point is on a reference in a ‘magit-refs-mode’ + buffer, in which case the behavior may be different, but only if + you have customized the option ‘magit-visit-ref-behavior’. + + -- User Option: magit-visit-ref-behavior + This option controls how ‘magit-visit-ref’ behaves in + ‘magit-refs-mode’ buffers. + + By default ‘magit-visit-ref’ behaves like ‘magit-show-commit’, in + all buffers, including ‘magit-refs-mode’ buffers. When the type of + the section at point is ‘commit’ then "RET" is bound to + ‘magit-show-commit’, and when the type is either ‘branch’ or ‘tag’ + then it is bound to ‘magit-visit-ref’. + + "RET" is one of Magit’s most essential keys and at least by default + it should behave consistently across all of Magit, especially + because users quickly learn that it does something very harmless; + it shows more information about the thing at point in another + buffer. + + However "RET" used to behave differently in ‘magit-refs-mode’ + buffers, doing surprising things, some of which cannot really be + described as "visit this thing". If you’ve grown accustomed this + behavior, you can restore it by adding one or more of the below + symbols to the value of this option. But keep in mind that by + doing so you don’t only introduce inconsistencies, you also lose + some functionality and might have to resort to ‘M-x + magit-show-commit’ to get it back. + + ‘magit-visit-ref’ looks for these symbols in the order in which + they are described here. If the presence of a symbol applies to + the current situation, then the symbols that follow do not affect + the outcome. + + • ‘focus-on-ref’ + + With a prefix argument update the buffer to show commit counts + and lists of cherry commits relative to the reference at point + instead of relative to the current buffer or ‘HEAD’. + + Instead of adding this symbol, consider pressing "C-u y o + RET". + + • ‘create-branch’ + + If point is on a remote branch, then create a new local branch + with the same name, use the remote branch as its upstream, and + then check out the local branch. + + Instead of adding this symbol, consider pressing "b c RET + RET", like you would do in other buffers. + + • ‘checkout-any’ + + Check out the reference at point. If that reference is a tag + or a remote branch, then this results in a detached ‘HEAD’. + + Instead of adding this symbol, consider pressing "b b RET", + like you would do in other buffers. + + • ‘checkout-branch’ + + Check out the local branch at point. + + Instead of adding this symbol, consider pressing "b b RET", + like you would do in other buffers. + +* Menu: + +* References Sections:: + + +File: magit.info, Node: References Sections, Up: References Buffer + +5.6.1 References Sections +------------------------- + +The contents of references buffers is controlled using the hook +‘magit-refs-sections-hook’. See *note Section Hooks:: to learn about +such hooks and how to customize them. All of the below functions are +members of the default value. Note that it makes much less sense to +customize this hook than it does for the respective hook used for the +status buffer. + + -- User Option: magit-refs-sections-hook + Hook run to insert sections into a references buffer. + + -- Function: magit-insert-local-branches + Insert sections showing all local branches. + + -- Function: magit-insert-remote-branches + Insert sections showing all remote-tracking branches. + + -- Function: magit-insert-tags + Insert sections showing all tags. + + +File: magit.info, Node: Bisecting, Next: Visiting Files and Blobs, Prev: References Buffer, Up: Inspecting + +5.7 Bisecting +============= + +Also see *note (gitman)git-bisect::. + +‘B’ (‘magit-bisect’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + + When bisecting is not in progress, then the transient features the +following suffix commands. + +‘B B’ (‘magit-bisect-start’) + Start a bisect session. + + Bisecting a bug means to find the commit that introduced it. This + command starts such a bisect session by asking for a known good + commit and a known bad commit. If you’re bisecting a change that + isn’t a regression, you can select alternate terms that are + conceptually more fitting than "bad" and "good", but the infix + arguments to do so are disabled by default. + +‘B s’ (‘magit-bisect-run’) + Bisect automatically by running commands after each step. + + When bisecting in progress, then the transient instead features the +following suffix commands. + +‘B b’ (‘magit-bisect-bad’) + Mark the current commit as bad. Use this after you have asserted + that the commit does contain the bug in question. + +‘B g’ (‘magit-bisect-good’) + Mark the current commit as good. Use this after you have asserted + that the commit does not contain the bug in question. + +‘B m’ (‘magit-bisect-mark’) + Mark the current commit with one of the bisect terms. This command + provides an alternative to ‘magit-bisect-bad’ and + ‘magit-bisect-good’ and is useful when using terms other than "bad" + and "good". This suffix is disabled by default. + +‘B k’ (‘magit-bisect-skip’) + Skip the current commit. Use this if for some reason the current + commit is not a good one to test. This command lets Git choose a + different one. + +‘B r’ (‘magit-bisect-reset’) + After bisecting, cleanup bisection state and return to original + ‘HEAD’. + + By default the status buffer shows information about the ongoing +bisect session. + + -- User Option: magit-bisect-show-graph + This option controls whether a graph is displayed for the log of + commits that still have to be bisected. + + +File: magit.info, Node: Visiting Files and Blobs, Next: Blaming, Prev: Bisecting, Up: Inspecting + +5.8 Visiting Files and Blobs +============================ + +Magit provides several commands that visit a file or blob (the version +of a file that is stored in a certain commit). Actually it provides +several *groups* of such commands and the several *variants* within each +group. + + Also see *note Commands for Buffers Visiting Files::. + +* Menu: + +* General-Purpose Visit Commands:: +* Visiting Files and Blobs from a Diff:: + + +File: magit.info, Node: General-Purpose Visit Commands, Next: Visiting Files and Blobs from a Diff, Up: Visiting Files and Blobs + +5.8.1 General-Purpose Visit Commands +------------------------------------ + +These commands can be used anywhere to open any blob. Currently no keys +are bound to these commands by default, but that is likely to change. + + -- Command: magit-find-file + This command reads a filename and revision from the user and visits + the respective blob in a buffer. The buffer is displayed in the + selected window. + + -- Command: magit-find-file-other-window + This command reads a filename and revision from the user and visits + the respective blob in a buffer. The buffer is displayed in + another window. + + -- Command: magit-find-file-other-frame + This command reads a filename and revision from the user and visits + the respective blob in a buffer. The buffer is displayed in + another frame. + + +File: magit.info, Node: Visiting Files and Blobs from a Diff, Prev: General-Purpose Visit Commands, Up: Visiting Files and Blobs + +5.8.2 Visiting Files and Blobs from a Diff +------------------------------------------ + +These commands can only be used when point is inside a diff. + +‘<RET>’ (‘magit-diff-visit-file’) + This command visits the appropriate version of the file that the + diff at point is about. + + This commands visits the worktree version of the appropriate file. + The location of point inside the diff determines which file is + being visited. The visited version depends on what changes the + diff is about. + + 1. If the diff shows uncommitted changes (i.e., staged or + unstaged changes), then visit the file in the working tree + (i.e., the same "real" file that ‘find-file’ would visit. In + all other cases visit a "blob" (i.e., the version of a file as + stored in some commit). + + 2. If point is on a removed line, then visit the blob for the + first parent of the commit that removed that line, i.e., the + last commit where that line still exists. + + 3. If point is on an added or context line, then visit the blob + that adds that line, or if the diff shows from more than a + single commit, then visit the blob from the last of these + commits. + + In the file-visiting buffer this command goes to the line that + corresponds to the line that point is on in the diff. + + The buffer is displayed in the selected window. With a prefix + argument the buffer is displayed in another window instead. + + -- User Option: magit-diff-visit-previous-blob + This option controls whether ‘magit-diff-visit-file’ may visit the + previous blob. When this is ‘t’ (the default) and point is on a + removed line in a diff for a committed change, then + ‘magit-diff-visit-file’ visits the blob from the last revision + which still had that line. + + Currently this is only supported for committed changes, for staged + and unstaged changes ‘magit-diff-visit-file’ always visits the file + in the working tree. + +‘C-<return>’ (‘magit-diff-visit-file-worktree’) + This command visits the worktree version of the appropriate file. + The location of point inside the diff determines which file is + being visited. Unlike ‘magit-diff-visit-file’ it always visits the + "real" file in the working tree, i.e the "current version" of the + file. + + In the file-visiting buffer this command goes to the line that + corresponds to the line that point is on in the diff. Lines that + were added or removed in the working tree, the index and other + commits in between are automatically accounted for. + + The buffer is displayed in the selected window. With a prefix + argument the buffer is displayed in another window instead. + + Variants of the above two commands exist that instead visit the file +in another window or in another frame. If you prefer such behavior, +then you may want to change the above key bindings, but note that the +above commands also use another window when invoked with a prefix +argument. + + -- Command: magit-diff-visit-file-other-window + -- Command: magit-diff-visit-file-other-frame + -- Command: magit-diff-visit-worktree-file-other-window + -- Command: magit-diff-visit-worktree-file-other-frame + + +File: magit.info, Node: Blaming, Prev: Visiting Files and Blobs, Up: Inspecting + +5.9 Blaming +=========== + +Also see *note (gitman)git-blame::. + + To start blaming, invoke the ‘magit-file-dispatch’ transient prefix +command. When using the default key bindings, that can be done by +pressing ‘C-c M-g’. When using the recommended bindings, this command +is instead bound to ‘C-c f’. Also see *note Global Bindings::. + + The blaming suffix commands can be invoked directly from the file +dispatch transient. However if you want to set an infix argument, then +you have to enter the blaming sub-prefix first. + +‘C-c f B’ (‘magit-blame’) +‘C-c f b’ (‘magit-blame-addition’) +‘C-c f B b’ +‘C-c f r’ (‘magit-blame-removal’) +‘C-c f B r’ +‘C-c f f’ (‘magit-blame-reverse’) +‘C-c f B f’ +‘C-c f e’ (‘magit-blame-echo’) +‘C-c f B e’ +‘C-c f q’ (‘magit-blame-quit’) +‘C-c f B q’ + Each of these commands is documented individually right below, + alongside their default key bindings. The bindings shown above are + the recommended bindings, which you can enable by following the + instructions in *note Global Bindings::. + +‘C-c M-g B’ (‘magit-blame’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + Note that not all of the following suffixes are available at all +times. For example if ‘magit-blame-mode’ is not enabled, then the +command whose purpose is to turn off that mode would not be of any use +and therefore isn’t available. + +‘C-c M-g b’ (‘magit-blame-addition’) +‘C-c M-g B b’ + This command augments each line or chunk of lines in the current + file-visiting or blob-visiting buffer with information about what + commits last touched these lines. + + If the buffer visits a revision of that file, then history up to + that revision is considered. Otherwise, the file’s full history is + considered, including uncommitted changes. + + If Magit-Blame mode is already turned on in the current buffer then + blaming is done recursively, by visiting REVISION:FILE (using + ‘magit-find-file’), where REVISION is a parent of the revision that + added the current line or chunk of lines. + +‘C-c M-g r’ (‘magit-blame-removal’) +‘C-c M-g B r’ + This command augments each line or chunk of lines in the current + blob-visiting buffer with information about the revision that + removes it. It cannot be used in file-visiting buffers. + + Like ‘magit-blame-addition’, this command can be used recursively. + +‘C-c M-g f’ (‘magit-blame-reverse’) +‘C-c M-g B f’ + This command augments each line or chunk of lines in the current + file-visiting or blob-visiting buffer with information about the + last revision in which a line still existed. + + Like ‘magit-blame-addition’, this command can be used recursively. + +‘C-c M-g e’ (‘magit-blame-echo’) +‘C-c M-g B e’ + This command is like ‘magit-blame-addition’ except that it doesn’t + turn on ‘read-only-mode’ and that it initially uses the + visualization style specified by option ‘magit-blame-echo-style’. + + The following key bindings are available when Magit-Blame mode is +enabled and Read-Only mode is not enabled. These commands are also +available in other buffers; here only the behavior is described that is +relevant in file-visiting buffers that are being blamed. + +‘C-c M-g q’ (‘magit-blame-quit’) +‘C-c M-g B q’ + This command turns off Magit-Blame mode. If the buffer was created + during a recursive blame, then it also kills the buffer. + +‘<RET>’ (‘magit-show-commit’) + This command shows the commit that last touched the line at point. + +‘<SPC>’ (‘magit-diff-show-or-scroll-up’) + This command updates the commit buffer. + + This either shows the commit that last touched the line at point in + the appropriate buffer, or if that buffer is already being + displayed in the current frame and if that buffer contains + information about that commit, then the buffer is scrolled up + instead. + +‘<DEL>’ (‘magit-diff-show-or-scroll-down’) + This command updates the commit buffer. + + This either shows the commit that last touched the line at point in + the appropriate buffer, or if that buffer is already being + displayed in the current frame and if that buffer contains + information about that commit, then the buffer is scrolled down + instead. + + The following key bindings are available when both Magit-Blame mode +and Read-Only mode are enabled. + +‘b’ (‘magit-blame’) + See above. + +‘n’ (‘magit-blame-next-chunk’) + This command moves to the next chunk. + +‘N’ (‘magit-blame-next-chunk-same-commit’) + This command moves to the next chunk from the same commit. + +‘p’ (‘magit-blame-previous-chunk’) + This command moves to the previous chunk. + +‘P’ (‘magit-blame-previous-chunk-same-commit’) + This command moves to the previous chunk from the same commit. + +‘q’ (‘magit-blame-quit’) + This command turns off Magit-Blame mode. If the buffer was created + during a recursive blame, then it also kills the buffer. + +‘M-w’ (‘magit-blame-copy-hash’) + This command saves the hash of the current chunk’s commit to the + kill ring. + + When the region is active, the command saves the region’s content + instead of the hash, like ‘kill-ring-save’ would. + +‘c’ (‘magit-blame-cycle-style’) + This command changes how blame information is visualized in the + current buffer by cycling through the styles specified using the + option ‘magit-blame-styles’. + + Blaming is also controlled using the following options. + + -- User Option: magit-blame-styles + This option defines a list of styles used to visualize blame + information. For now see its doc-string to learn more. + + -- User Option: magit-blame-echo-style + This option specifies the blame visualization style used by the + command ‘magit-blame-echo’. This must be a symbol that is used as + the identifier for one of the styles defined in + ‘magit-blame-styles’. + + -- User Option: magit-blame-time-format + This option specifies the format string used to display times when + showing blame information. + + -- User Option: magit-blame-read-only + This option controls whether blaming a buffer also makes + temporarily read-only. + + -- User Option: magit-blame-disable-modes + This option lists incompatible minor-modes that should be disabled + temporarily when a buffer contains blame information. They are + enabled again when the buffer no longer shows blame information. + + -- User Option: magit-blame-goto-chunk-hook + This hook is run when moving between chunks. + + +File: magit.info, Node: Manipulating, Next: Transferring, Prev: Inspecting, Up: Top + +6 Manipulating +************** + +* Menu: + +* Creating Repository:: +* Cloning Repository:: +* Staging and Unstaging:: +* Applying:: +* Committing:: +* Branching:: +* Merging:: +* Resolving Conflicts:: +* Rebasing:: +* Cherry Picking:: +* Resetting:: +* Stashing:: + + +File: magit.info, Node: Creating Repository, Next: Cloning Repository, Up: Manipulating + +6.1 Creating Repository +======================= + +‘I’ (‘magit-init’) + This command initializes a repository and then shows the status + buffer for the new repository. + + If the directory is below an existing repository, then the user has + to confirm that a new one should be created inside. If the + directory is the root of the existing repository, then the user has + to confirm that it should be reinitialized. + + +File: magit.info, Node: Cloning Repository, Next: Staging and Unstaging, Prev: Creating Repository, Up: Manipulating + +6.2 Cloning Repository +====================== + +To clone a remote or local repository use ‘C’, which is bound to the +command ‘magit-clone’. This command either act as a transient prefix +command, which binds several infix arguments and suffix commands, or it +can invoke ‘git clone’ directly, depending on whether a prefix argument +is used and on the value of ‘magit-clone-always-transient’. + + -- User Option: magit-clone-always-transient + This option controls whether the command ‘magit-clone’ always acts + as a transient prefix command, regardless of whether a prefix + argument is used or not. If ‘t’, then that command always acts as + a transient prefix. If ‘nil’, then a prefix argument has to be + used for it to act as a transient. + +‘C’ (‘magit-clone’) + This command either acts as a transient prefix command as described + above or does the same thing as ‘transient-clone-regular’ as + described below. + + If it acts as a transient prefix, then it binds the following + suffix commands and several infix arguments. + +‘C C’ (‘magit-clone-regular’) + This command creates a regular clone of an existing repository. + The repository and the target directory are read from the user. + +‘C s’ (‘magit-clone-shallow’) + This command creates a shallow clone of an existing repository. + The repository and the target directory are read from the user. By + default the depth of the cloned history is a single commit, but + with a prefix argument the depth is read from the user. + +‘C >’ (‘magit-clone-sparse’) + This command creates a clone of an existing repository and + initializes a sparse checkout, avoiding a checkout of the full + working tree. To add more directories, use the + ‘magit-sparse-checkout’ transient (see *note Sparse checkouts::). + +‘C b’ (‘magit-clone-bare’) + This command creates a bare clone of an existing repository. The + repository and the target directory are read from the user. + +‘C m’ (‘magit-clone-mirror’) + This command creates a mirror of an existing repository. The + repository and the target directory are read from the user. + + The following suffixes are disabled by default. See *note +(transient)Enabling and Disabling Suffixes:: for how to enable them. + +‘C d’ (‘magit-clone-shallow-since’) + This command creates a shallow clone of an existing repository. + Only commits that were committed after a date are cloned, which is + read from the user. The repository and the target directory are + also read from the user. + +‘C e’ (‘magit-clone-shallow-exclude’) + This command creates a shallow clone of an existing repository. + This reads a branch or tag from the user. Commits that are + reachable from that are not cloned. The repository and the target + directory are also read from the user. + + -- User Option: magit-clone-set-remote-head + This option controls whether cloning causes the reference + ‘refs/remotes/<remote>/HEAD’ to be created in the clone. The + default is to delete the reference after running ‘git clone’, which + insists on creating it. This is because the reference has not been + found to be particularly useful as it is not automatically updated + when the ‘HEAD’ of the remote changes. Setting this option to ‘t’ + preserves Git’s default behavior of creating the reference. + + -- User Option: magit-clone-set-remote.pushDefault + This option controls whether the value of the Git variable + ‘remote.pushDefault’ is set after cloning. + + • If ‘t’, then it is always set without asking. + • If ‘ask’, then the users are asked every time they clone a + repository. + • If ‘nil’, then it is never set. + + -- User Option: magit-clone-default-directory + This option control the default directory name used when reading + the destination for a cloning operation. + + • If ‘nil’ (the default), then the value of ‘default-directory’ + is used. + • If a directory, then that is used. + • If a function, then that is called with the remote url as the + only argument and the returned value is used. + + -- User Option: magit-clone-name-alist + This option maps regular expressions, which match repository names, + to repository urls, making it possible for users to enter short + names instead of urls when cloning repositories. + + Each element has the form ‘(REGEXP HOSTNAME USER)’. When the user + enters a name when a cloning command asks for a name or url, then + that is looked up in this list. The first element whose REGEXP + matches is used. + + The format specified by option ‘magit-clone-url-format’ is used to + turn the name into an url, using HOSTNAME and the repository name. + If the provided name contains a slash, then that is used. + Otherwise if the name omits the owner of the repository, then the + default user specified in the matched entry is used. + + If USER contains a dot, then it is treated as a Git variable and + the value of that is used as the username. Otherwise it is used as + the username itself. + + -- User Option: magit-clone-url-format + The format specified by this option is used when turning repository + names into urls. ‘%h’ is the hostname and ‘%n’ is the repository + name, including the name of the owner. The value can be a string + (representing a single static format) or an alist with elements + ‘(HOSTNAME . FORMAT)’ mapping hostnames to formats. When an alist + is used, the ‘t’ key represents the default format. + + Example of a single format string: + + (setq magit-clone-url-format + "git@%h:%n.git") + + Example of by-hostname format strings: + + (setq magit-clone-url-format + '(("git.example.com" . "git@%h:~%n") + (nil . "git@%h:%n.git"))) + + -- User Option: magit-post-clone-hook + Hook run after the Git process has successfully finished cloning + the repository. When the hook is called, ‘default-directory’ is + let-bound to the directory where the repository has been cloned. + + +File: magit.info, Node: Staging and Unstaging, Next: Applying, Prev: Cloning Repository, Up: Manipulating + +6.3 Staging and Unstaging +========================= + +Like Git, Magit can of course stage and unstage complete files. Unlike +Git, it also allows users to gracefully un-/stage individual hunks and +even just part of a hunk. To stage individual hunks and parts of hunks +using Git directly, one has to use the very modal and rather clumsy +interface of a ‘git add --interactive’ session. + + With Magit, on the other hand, one can un-/stage individual hunks by +just moving point into the respective section inside a diff displayed in +the status buffer or a separate diff buffer and typing ‘s’ or ‘u’. To +operate on just parts of a hunk, mark the changes that should be +un-/staged using the region and then press the same key that would be +used to un-/stage. To stage multiple files or hunks at once use a +region that starts inside the heading of such a section and ends inside +the heading of a sibling section of the same type. + + Besides staging and unstaging, Magit also provides several other +"apply variants" that can also operate on a file, multiple files at +once, a hunk, multiple hunks at once, and on parts of a hunk. These +apply variants are described in the next section. + + You can also use Ediff to stage and unstage. See *note Ediffing::. + +‘s’ (‘magit-stage’) + Add the change at point to the staging area. + + With a prefix argument and an untracked file (or files) at point, + stage the file but not its content. This makes it possible to + stage only a subset of the new file’s changes. + +‘S’ (‘magit-stage-modified’) + Stage all changes to files modified in the worktree. Stage all new + content of tracked files and remove tracked files that no longer + exist in the working tree from the index also. With a prefix + argument also stage previously untracked (but not ignored) files. + +‘u’ (‘magit-unstage’) + Remove the change at point from the staging area. + + Only staged changes can be unstaged. But by default this command + performs an action that is somewhat similar to unstaging, when it + is called on a committed change: it reverses the change in the + index but not in the working tree. + +‘U’ (‘magit-unstage-all’) + Remove all changes from the staging area. + + -- User Option: magit-unstage-committed + This option controls whether ‘magit-unstage’ "unstages" committed + changes by reversing them in the index but not the working tree. + The alternative is to raise an error. + +‘M-x magit-reverse-in-index’ + This command reverses the committed change at point in the index + but not the working tree. By default no key is bound directly to + this command, but it is indirectly called when ‘u’ + (‘magit-unstage’) is pressed on a committed change. + + This allows extracting a change from ‘HEAD’, while leaving it in + the working tree, so that it can later be committed using a + separate commit. A typical workflow would be: + + 1. Optionally make sure that there are no uncommitted changes. + 2. Visit the ‘HEAD’ commit and navigate to the change that should + not have been included in that commit. + 3. Type ‘u’ (‘magit-unstage’) to reverse it in the index. This + assumes that ‘magit-unstage-committed-changes’ is non-nil. + 4. Type ‘c e’ to extend ‘HEAD’ with the staged changes, including + those that were already staged before. + 5. Optionally stage the remaining changes using ‘s’ or ‘S’ and + then type ‘c c’ to create a new commit. + +‘M-x magit-reset-index’ + Reset the index to some commit. The commit is read from the user + and defaults to the commit at point. If there is no commit at + point, then it defaults to ‘HEAD’. + +* Menu: + +* Staging from File-Visiting Buffers:: + + +File: magit.info, Node: Staging from File-Visiting Buffers, Up: Staging and Unstaging + +6.3.1 Staging from File-Visiting Buffers +---------------------------------------- + +Fine-grained un-/staging has to be done from the status or a diff +buffer, but it’s also possible to un-/stage all changes made to the file +visited in the current buffer right from inside that buffer. + +‘M-x magit-stage-file’ + When invoked inside a file-visiting buffer, then stage all changes + to that file. In a Magit buffer, stage the file at point if any. + Otherwise prompt for a file to be staged. With a prefix argument + always prompt the user for a file, even in a file-visiting buffer + or when there is a file section at point. + +‘M-x magit-unstage-file’ + When invoked inside a file-visiting buffer, then unstage all + changes to that file. In a Magit buffer, unstage the file at point + if any. Otherwise prompt for a file to be unstaged. With a prefix + argument always prompt the user for a file, even in a file-visiting + buffer or when there is a file section at point. + + +File: magit.info, Node: Applying, Next: Committing, Prev: Staging and Unstaging, Up: Manipulating + +6.4 Applying +============ + +Magit provides several "apply variants": stage, unstage, discard, +reverse, and "regular apply". At least when operating on a hunk they +are all implemented using ‘git apply’, which is why they are called +"apply variants". + + • Stage. Apply a change from the working tree to the index. The + change also remains in the working tree. + + • Unstage. Remove a change from the index. The change remains in + the working tree. + + • Discard. On a staged change, remove it from the working tree and + the index. On an unstaged change, remove it from the working tree + only. + + • Reverse. Reverse a change in the working tree. Both committed and + staged changes can be reversed. Unstaged changes cannot be + reversed. Discard them instead. + + • Apply. Apply a change to the working tree. Both committed and + staged changes can be applied. Unstaged changes cannot be applied + - as they already have been applied. + + The previous section described the staging and unstaging commands. +What follows are the commands which implement the remaining apply +variants. + +‘a’ (‘magit-apply’) + Apply the change at point to the working tree. + + With a prefix argument fallback to a 3-way merge. Doing so causes + the change to be applied to the index as well. + +‘k’ (‘magit-discard’) + Remove the change at point from the working tree. + + On a hunk or file with unresolved conflicts prompt which side to + keep (while discarding the other). If point is within the text of + a side, then keep that side without prompting. + +‘v’ (‘magit-reverse’) + Reverse the change at point in the working tree. + + With a prefix argument fallback to a 3-way merge. Doing so causes + the change to be applied to the index as well. + + With a prefix argument all apply variants attempt a 3-way merge when +appropriate (i.e., when ‘git apply’ is used internally). + + +File: magit.info, Node: Committing, Next: Branching, Prev: Applying, Up: Manipulating + +6.5 Committing +============== + +When the user initiates a commit, Magit calls ‘git commit’ without any +arguments, so Git has to get it from the user. It creates the file +‘.git/COMMIT_EDITMSG’ and then opens that file in an editor. Magit +arranges for that editor to be the Emacsclient. Once the user finishes +the editing session, the Emacsclient exits and Git creates the commit +using the file’s content as message. + +* Menu: + +* Initiating a Commit:: +* Editing Commit Messages:: + + +File: magit.info, Node: Initiating a Commit, Next: Editing Commit Messages, Up: Committing + +6.5.1 Initiating a Commit +------------------------- + +Also see *note (gitman)git-commit::. + +‘c’ (‘magit-commit’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘c c’ (‘magit-commit-create’) + Create a new commit on ‘HEAD’. With a prefix argument amend to the + commit at ‘HEAD’ instead. + +‘c a’ (‘magit-commit-amend’) + Amend the last commit. + +‘c e’ (‘magit-commit-extend’) + Amend the last commit, without editing the message. With a prefix + argument keep the committer date, otherwise change it. The option + ‘magit-commit-extend-override-date’ can be used to inverse the + meaning of the prefix argument. + + Non-interactively respect the optional OVERRIDE-DATE argument and + ignore the option. + +‘c w’ (‘magit-commit-reword’) + Reword the last commit, ignoring staged changes. With a prefix + argument keep the committer date, otherwise change it. The option + ‘magit-commit-reword-override-date’ can be used to inverse the + meaning of the prefix argument. + + Non-interactively respect the optional OVERRIDE-DATE argument and + ignore the option. + +‘c f’ (‘magit-commit-fixup’) + Create a fixup commit. + + With a prefix argument the target commit has to be confirmed. + Otherwise the commit at point may be used without confirmation + depending on the value of option ‘magit-commit-squash-confirm’. + +‘c F’ (‘magit-commit-instant-fixup’) + Create a fixup commit and instantly rebase. + +‘c s’ (‘magit-commit-squash’) + Create a squash commit, without editing the squash message. + + With a prefix argument the target commit has to be confirmed. + Otherwise the commit at point may be used without confirmation + depending on the value of option ‘magit-commit-squash-confirm’. + +‘c S’ (‘magit-commit-instant-squash’) + Create a squash commit and instantly rebase. + +‘c A’ (‘magit-commit-augment’) + Create a squash commit, editing the squash message. + + With a prefix argument the target commit has to be confirmed. + Otherwise the commit at point may be used without confirmation + depending on the value of option ‘magit-commit-squash-confirm’. + + -- User Option: magit-commit-ask-to-stage + Whether to ask to stage all unstaged changes when committing and + nothing is staged. + + -- User Option: magit-commit-show-diff + Whether the relevant diff is automatically shown when committing. + + -- User Option: magit-commit-extend-override-date + Whether using ‘magit-commit-extend’ changes the committer date. + + -- User Option: magit-commit-reword-override-date + Whether using ‘magit-commit-reword’ changes the committer date. + + -- User Option: magit-commit-squash-confirm + Whether the commit targeted by squash and fixup has to be + confirmed. When non-nil then the commit at point (if any) is used + as default choice. Otherwise it has to be confirmed. This option + only affects ‘magit-commit-squash’ and ‘magit-commit-fixup’. The + "instant" variants always require confirmation because making an + error while using those is harder to recover from. + + -- User Option: magit-post-commit-hook + Hook run after creating a commit without the user editing a + message. + + This hook is run by ‘magit-refresh’ if ‘this-command’ is a member + of ‘magit-post-commit-hook-commands’. This only includes commands + named ‘magit-commit-*’ that do *not* require that the user edits + the commit message in a buffer. + + Also see ‘git-commit-post-finish-hook’. + + -- User Option: magit-commit-diff-inhibit-same-window + Whether to inhibit use of same window when showing diff while + committing. + + When writing a commit, then a diff of the changes to be committed + is automatically shown. The idea is that the diff is shown in a + different window of the same frame and for most users that just + works. In other words most users can completely ignore this option + because its value doesn’t make a difference for them. + + However for users who configured Emacs to never create a new window + even when the package explicitly tries to do so, then displaying + two new buffers necessarily means that the first is immediately + replaced by the second. In our case the message buffer is + immediately replaced by the diff buffer, which is of course highly + undesirable. + + A workaround is to suppress this user configuration in this + particular case. Users have to explicitly opt-in by toggling this + option. We cannot enable the workaround unconditionally because + that again causes issues for other users: if the frame is too tiny + or the relevant settings too aggressive, then the diff buffer would + end up being displayed in a new frame. + + Also see <https://github.com/magit/magit/issues/4132>. + + +File: magit.info, Node: Editing Commit Messages, Prev: Initiating a Commit, Up: Committing + +6.5.2 Editing Commit Messages +----------------------------- + +After initiating a commit as described in the previous section, two new +buffers appear. One shows the changes that are about to be committed, +while the other is used to write the message. + + Commit messages are edited in an edit session - in the background +‘git’ is waiting for the editor, in our case ‘emacsclient’, to save the +commit message in a file (in most cases ‘.git/COMMIT_EDITMSG’) and then +return. If the editor returns with a non-zero exit status then ‘git’ +does not create the commit. So the most important commands are those +for finishing and aborting the commit. + +‘C-c C-c’ (‘with-editor-finish’) + Finish the current editing session by returning with exit code 0. + Git then creates the commit using the message it finds in the file. + +‘C-c C-k’ (‘with-editor-cancel’) + Cancel the current editing session by returning with exit code 1. + Git then cancels the commit, but leaves the file untouched. + + In addition to being used by ‘git commit’, messages may also be +stored in a ring that persists until Emacs is closed. By default the +message is stored at the beginning and the end of an edit session +(regardless of whether the session is finished successfully or was +canceled). It is sometimes useful to bring back messages from that +ring. + +‘C-c M-s’ (‘git-commit-save-message’) + Save the current buffer content to the commit message ring. + +‘M-p’ (‘git-commit-prev-message’) + Cycle backward through the commit message ring, after saving the + current message to the ring. With a numeric prefix ARG, go back + ARG comments. + +‘M-n’ (‘git-commit-next-message’) + Cycle forward through the commit message ring, after saving the + current message to the ring. With a numeric prefix ARG, go back + ARG comments. + + By default the diff for the changes that are about to be committed +are automatically shown when invoking the commit. To prevent that, +remove ‘magit-commit-diff’ from ‘server-switch-hook’. + + When amending to an existing commit it may be useful to show either +the changes that are about to be added to that commit or to show those +changes alongside those that have already been committed. + +‘C-c C-d’ (‘magit-diff-while-committing’) + While committing, show the changes that are about to be committed. + While amending, invoking the command again toggles between showing + just the new changes or all the changes that will be committed. + +* Menu: + +* Using the Revision Stack:: +* Commit Pseudo Headers:: +* Commit Mode and Hooks:: +* Commit Message Conventions:: + + +File: magit.info, Node: Using the Revision Stack, Next: Commit Pseudo Headers, Up: Editing Commit Messages + +Using the Revision Stack +........................ + +‘C-c C-w’ (‘magit-pop-revision-stack’) + This command inserts a representation of a revision into the + current buffer. It can be used inside buffers used to write commit + messages but also in other buffers such as buffers used to edit + emails or ChangeLog files. + + By default this command pops the revision which was last added to + the ‘magit-revision-stack’ and inserts it into the current buffer + according to ‘magit-pop-revision-stack-format’. Revisions can be + put on the stack using ‘magit-copy-section-value’ and + ‘magit-copy-buffer-revision’. + + If the stack is empty or with a prefix argument it instead reads a + revision in the minibuffer. By using the minibuffer history this + allows selecting an item which was popped earlier or to insert an + arbitrary reference or revision without first pushing it onto the + stack. + + When reading the revision from the minibuffer, then it might not be + possible to guess the correct repository. When this command is + called inside a repository (e.g., while composing a commit + message), then that repository is used. Otherwise (e.g., while + composing an email) then the repository recorded for the top + element of the stack is used (even though we insert another + revision). If not called inside a repository and with an empty + stack, or with two prefix arguments, then read the repository in + the minibuffer too. + + -- User Option: magit-pop-revision-stack-format + This option controls how the command ‘magit-pop-revision-stack’ + inserts a revision into the current buffer. + + The entries on the stack have the format ‘(HASH TOPLEVEL)’ and this + option has the format ‘(POINT-FORMAT EOB-FORMAT INDEX-REGEXP)’, all + of which may be nil or a string (though either one of EOB-FORMAT or + POINT-FORMAT should be a string, and if INDEX-REGEXP is non-nil, + then the two formats should be too). + + First INDEX-REGEXP is used to find the previously inserted entry, + by searching backward from point. The first submatch must match + the index number. That number is incremented by one, and becomes + the index number of the entry to be inserted. If you don’t want to + number the inserted revisions, then use nil for INDEX-REGEXP. + + If INDEX-REGEXP is non-nil then both POINT-FORMAT and EOB-FORMAT + should contain \"%N\", which is replaced with the number that was + determined in the previous step. + + Both formats, if non-nil and after removing %N, are then expanded + using ‘git show --format=FORMAT ...’ inside TOPLEVEL. + + The expansion of POINT-FORMAT is inserted at point, and the + expansion of EOB-FORMAT is inserted at the end of the buffer (if + the buffer ends with a comment, then it is inserted right before + that). + + +File: magit.info, Node: Commit Pseudo Headers, Next: Commit Mode and Hooks, Prev: Using the Revision Stack, Up: Editing Commit Messages + +Commit Pseudo Headers +..................... + +Some projects use pseudo headers in commit messages. Magit colorizes +such headers and provides some commands to insert such headers. + + -- User Option: git-commit-known-pseudo-headers + A list of Git pseudo headers to be highlighted. + +‘C-c C-i’ (‘git-commit-insert-pseudo-header’) + Insert a commit message pseudo header. + +‘C-c C-a’ (‘git-commit-ack’) + Insert a header acknowledging that you have looked at the commit. + +‘C-c C-r’ (‘git-commit-review’) + Insert a header acknowledging that you have reviewed the commit. + +‘C-c C-s’ (‘git-commit-signoff’) + Insert a header to sign off the commit. + +‘C-c C-t’ (‘git-commit-test’) + Insert a header acknowledging that you have tested the commit. + +‘C-c C-o’ (‘git-commit-cc’) + Insert a header mentioning someone who might be interested. + +‘C-c C-p’ (‘git-commit-reported’) + Insert a header mentioning the person who reported the issue being + fixed by the commit. + +‘C-c M-i’ (‘git-commit-suggested’) + Insert a header mentioning the person who suggested the change. + + +File: magit.info, Node: Commit Mode and Hooks, Next: Commit Message Conventions, Prev: Commit Pseudo Headers, Up: Editing Commit Messages + +Commit Mode and Hooks +..................... + +‘git-commit-mode’ is a minor mode that is only used to establish certain +key bindings. This makes it possible to use an arbitrary major mode in +buffers used to edit commit messages. It is even possible to use +different major modes in different repositories, which is useful when +different projects impose different commit message conventions. + + -- User Option: git-commit-major-mode + The value of this option is the major mode used to edit Git commit + messages. + + Because ‘git-commit-mode’ is a minor mode, we don’t use its mode hook +to setup the buffer, except for the key bindings. All other setup +happens in the function ‘git-commit-setup’, which among other things +runs the hook ‘git-commit-setup-hook’. + + -- User Option: git-commit-setup-hook + Hook run at the end of ‘git-commit-setup’. + +The following functions are suitable for this hook: + + -- Function: git-commit-save-message + Save the current buffer content to the commit message ring. + + -- Function: git-commit-setup-changelog-support + After this function is called, ChangeLog entries are treated as + paragraphs. + + -- Function: git-commit-turn-on-auto-fill + Turn on ‘auto-fill-mode’. + + -- Function: git-commit-turn-on-flyspell + Turn on Flyspell mode. Also prevent comments from being checked + and finally check current non-comment text. + + -- Function: git-commit-propertize-diff + Propertize the diff shown inside the commit message buffer. Git + inserts such diffs into the commit message template when the + ‘--verbose’ argument is used. ‘magit-commit’ by default does not + offer that argument because the diff that is shown in a separate + buffer is more useful. But some users disagree, which is why this + function exists. + + -- Function: bug-reference-mode + Hyperlink bug references in the buffer. + + -- Function: with-editor-usage-message + Show usage information in the echo area. + + -- User Option: git-commit-post-finish-hook + Hook run after the user finished writing a commit message. + + This hook is only run after pressing ‘C-c C-c’ in a buffer used to + edit a commit message. If a commit is created without the user + typing a message into a buffer, then this hook is not run. + + This hook is not run until the new commit has been created. If + doing so takes Git longer than one second, then this hook isn’t run + at all. For certain commands such as ‘magit-rebase-continue’ this + hook is never run because doing so would lead to a race condition. + + This hook is only run if ‘magit’ is available. + + Also see ‘magit-post-commit-hook’. + + +File: magit.info, Node: Commit Message Conventions, Prev: Commit Mode and Hooks, Up: Editing Commit Messages + +Commit Message Conventions +.......................... + +Git-Commit highlights certain violations of commonly accepted commit +message conventions. Certain violations even cause Git-Commit to ask +you to confirm that you really want to do that. This nagging can of +course be turned off, but the result of doing that usually is that +instead of some code it’s now the human who is reviewing your commits +who has to waste some time telling you to fix your commits. + + -- User Option: git-commit-summary-max-length + The intended maximal length of the summary line of commit messages. + Characters beyond this column are colorized to indicate that this + preference has been violated. + + -- User Option: git-commit-finish-query-functions + List of functions called to query before performing commit. + + The commit message buffer is current while the functions are + called. If any of them returns nil, then the commit is not + performed and the buffer is not killed. The user should then fix + the issue and try again. + + The functions are called with one argument. If it is non-nil then + that indicates that the user used a prefix argument to force + finishing the session despite issues. Functions should usually + honor this wish and return non-nil. + + By default the only member is ‘git-commit-check-style-conventions’. + + -- Function: git-commit-check-style-conventions + This function checks for violations of certain basic style + conventions. For each violation it asks users if they want to + proceed anyway. + + -- User Option: git-commit-style-convention-checks + This option controls what conventions the function by the same name + tries to enforce. The value is a list of self-explanatory symbols + identifying certain conventions; ‘non-empty-second-line’ and + ‘overlong-summary-line’. + + +File: magit.info, Node: Branching, Next: Merging, Prev: Committing, Up: Manipulating + +6.6 Branching +============= + +* Menu: + +* The Two Remotes:: +* Branch Commands:: +* Branch Git Variables:: +* Auxiliary Branch Commands:: + + +File: magit.info, Node: The Two Remotes, Next: Branch Commands, Up: Branching + +6.6.1 The Two Remotes +--------------------- + +The upstream branch of some local branch is the branch into which the +commits on that local branch should eventually be merged, usually +something like ‘origin/master’. For the ‘master’ branch itself the +upstream branch and the branch it is being pushed to, are usually the +same remote branch. But for a feature branch the upstream branch and +the branch it is being pushed to should differ. + + The commits on feature branches too should _eventually_ end up in a +remote branch such as ‘origin/master’ or ‘origin/maint’. Such a branch +should therefore be used as the upstream. But feature branches +shouldn’t be pushed directly to such branches. Instead a feature branch +‘my-feature’ is usually pushed to ‘my-fork/my-feature’ or if you are a +contributor ‘origin/my-feature’. After the new feature has been +reviewed, the maintainer merges the feature into ‘master’. And finally +‘master’ (not ‘my-feature’ itself) is pushed to ‘origin/master’. + + But new features seldom are perfect on the first try, and so feature +branches usually have to be reviewed, improved, and re-pushed several +times. Pushing should therefore be easy to do, and for that reason many +Git users have concluded that it is best to use the remote branch to +which the local feature branch is being pushed as its upstream. + + But luckily Git has long ago gained support for a push-remote which +can be configured separately from the upstream branch, using the +variables ‘branch.<name>.pushRemote’ and ‘remote.pushDefault’. So we no +longer have to choose which of the two remotes should be used as "the +remote". + + Each of the fetching, pulling, and pushing transient commands +features three suffix commands that act on the current branch and some +other branch. Of these, ‘p’ is bound to a command which acts on the +push-remote, ‘u’ is bound to a command which acts on the upstream, and +‘e’ is bound to a command which acts on any other branch. The status +buffer shows unpushed and unpulled commits for both the push-remote and +the upstream. + + It’s fairly simple to configure these two remotes. The values of all +the variables that are related to fetching, pulling, and pushing (as +well as some other branch-related variables) can be inspected and +changed using the command ‘magit-branch-configure’, which is available +from many transient prefix commands that deal with branches. It is also +possible to set the push-remote or upstream while pushing (see *note +Pushing::). + + +File: magit.info, Node: Branch Commands, Next: Branch Git Variables, Prev: The Two Remotes, Up: Branching + +6.6.2 Branch Commands +--------------------- + +The transient prefix command ‘magit-branch’ is used to create and +checkout branches, and to make changes to existing branches. It is not +used to fetch, pull, merge, rebase, or push branches, i.e., this command +deals with branches themselves, not with the commits reachable from +them. Those features are available from separate transient command. + +‘b’ (‘magit-branch’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + + By default it also binds and displays the values of some + branch-related Git variables and allows changing their values. + + -- User Option: magit-branch-direct-configure + This option controls whether the transient command ‘magit-branch’ + can be used to directly change the values of Git variables. This + defaults to ‘t’ (to avoid changing key bindings). When set to + ‘nil’, then no variables are displayed by that transient command, + and its suffix command ‘magit-branch-configure’ has to be used + instead to view and change branch related variables. + +‘b C’ (‘magit-branch-configure’) +‘f C’ +‘F C’ +‘P C’ + This transient prefix command binds commands that set the value of + branch-related variables and displays them in a temporary buffer + until the transient is exited. + + With a prefix argument, this command always prompts for a branch. + + Without a prefix argument this depends on whether it was invoked as + a suffix of ‘magit-branch’ and on the + ‘magit-branch-direct-configure’ option. If ‘magit-branch’ already + displays the variables for the current branch, then it isn’t useful + to invoke another transient that displays them for the same branch. + In that case this command prompts for a branch. + + The variables are described in *note Branch Git Variables::. + +‘b b’ (‘magit-checkout’) + Checkout a revision read in the minibuffer and defaulting to the + branch or arbitrary revision at point. If the revision is a local + branch then that becomes the current branch. If it is something + else then ‘HEAD’ becomes detached. Checkout fails if the working + tree or the staging area contain changes. + +‘b n’ (‘magit-branch-create’) + Create a new branch. The user is asked for a branch or arbitrary + revision to use as the starting point of the new branch. When a + branch name is provided, then that becomes the upstream branch of + the new branch. The name of the new branch is also read in the + minibuffer. + + Also see option ‘magit-branch-prefer-remote-upstream’. + +‘b c’ (‘magit-branch-and-checkout’) + This command creates a new branch like ‘magit-branch-create’, but + then also checks it out. + + Also see option ‘magit-branch-prefer-remote-upstream’. + +‘b l’ (‘magit-branch-checkout’) + This command checks out an existing or new local branch. It reads + a branch name from the user offering all local branches and a + subset of remote branches as candidates. Remote branches for which + a local branch by the same name exists are omitted from the list of + candidates. The user can also enter a completely new branch name. + + • If the user selects an existing local branch, then that is + checked out. + + • If the user selects a remote branch, then it creates and + checks out a new local branch with the same name, and + configures the selected remote branch as the push target. + + • If the user enters a new branch name, then it creates and + checks that out, after also reading the starting-point from + the user. + + In the latter two cases the upstream is also set. Whether it is + set to the chosen starting point or something else depends on the + value of ‘magit-branch-adjust-remote-upstream-alist’. + +‘b s’ (‘magit-branch-spinoff’) + This command creates and checks out a new branch starting at and + tracking the current branch. That branch in turn is reset to the + last commit it shares with its upstream. If the current branch has + no upstream or no unpushed commits, then the new branch is created + anyway and the previously current branch is not touched. + + This is useful to create a feature branch after work has already + began on the old branch (likely but not necessarily "master"). + + If the current branch is a member of the value of option + ‘magit-branch-prefer-remote-upstream’ (which see), then the current + branch will be used as the starting point as usual, but the + upstream of the starting-point may be used as the upstream of the + new branch, instead of the starting-point itself. + + If optional FROM is non-nil, then the source branch is reset to + ‘FROM~’, instead of to the last commit it shares with its upstream. + Interactively, FROM is only ever non-nil, if the region selects + some commits, and among those commits, FROM is the commit that is + the fewest commits ahead of the source branch. + + The commit at the other end of the selection actually does not + matter, all commits between FROM and ‘HEAD’ are moved to the new + branch. If FROM is not reachable from ‘HEAD’ or is reachable from + the source branch’s upstream, then an error is raised. + +‘b S’ (‘magit-branch-spinout’) + This command behaves like ‘magit-branch-spinoff’, except that it + does not change the current branch. If there are any uncommitted + changes, then it behaves exactly like ‘magit-branch-spinoff’. + +‘b x’ (‘magit-branch-reset’) + This command resets a branch, defaulting to the branch at point, to + the tip of another branch or any other commit. + + When the branch being reset is the current branch, then a hard + reset is performed. If there are any uncommitted changes, then the + user has to confirm the reset because those changes would be lost. + + This is useful when you have started work on a feature branch but + realize it’s all crap and want to start over. + + When resetting to another branch and a prefix argument is used, + then the target branch is set as the upstream of the branch that is + being reset. + +‘b k’ (‘magit-branch-delete’) + Delete one or multiple branches. If the region marks multiple + branches, then offer to delete those. Otherwise, prompt for a + single branch to be deleted, defaulting to the branch at point. + + Require confirmation when deleting branches is dangerous in some + way. Option ‘magit-no-confirm’ can be customized to not require + confirmation in certain cases. See its docstring to learn why + confirmation is required by default in certain cases or if a prompt + is confusing. + +‘b m’ (‘magit-branch-rename’) + Rename a branch. The branch and the new name are read in the + minibuffer. With prefix argument the branch is renamed even if + that name conflicts with an existing branch. + + -- User Option: magit-branch-read-upstream-first + When creating a branch, whether to read the upstream branch before + the name of the branch that is to be created. The default is ‘t’, + and I recommend you leave it at that. + + -- User Option: magit-branch-prefer-remote-upstream + This option specifies whether remote upstreams are favored over + local upstreams when creating new branches. + + When a new branch is created, then the branch, commit, or stash at + point is suggested as the starting point of the new branch, or if + there is no such revision at point the current branch. In either + case the user may choose another starting point. + + If the chosen starting point is a branch, then it may also be set + as the upstream of the new branch, depending on the value of the + Git variable ‘branch.autoSetupMerge’. By default this is done for + remote branches, but not for local branches. + + You might prefer to always use some remote branch as upstream. If + the chosen starting point is (1) a local branch, (2) whose name + matches a member of the value of this option, (3) the upstream of + that local branch is a remote branch with the same name, and (4) + that remote branch can be fast-forwarded to the local branch, then + the chosen branch is used as starting point, but its own upstream + is used as the upstream of the new branch. + + Members of this option’s value are treated as branch names that + have to match exactly unless they contain a character that makes + them invalid as a branch name. Recommended characters to use to + trigger interpretation as a regexp are "*" and "^". Some other + characters which you might expect to be invalid, actually are not, + e.g., ".+$" are all perfectly valid. More precisely, if ‘git + check-ref-format --branch STRING’ exits with a non-zero status, + then treat STRING as a regexp. + + Assuming the chosen branch matches these conditions you would end + up with with e.g.: + + feature --upstream--> origin/master + + instead of + + feature --upstream--> master --upstream--> origin/master + + Which you prefer is a matter of personal preference. If you do + prefer the former, then you should add branches such as ‘master’, + ‘next’, and ‘maint’ to the value of this options. + + -- User Option: magit-branch-adjust-remote-upstream-alist + The value of this option is an alist of branches to be used as the + upstream when branching a remote branch. + + When creating a local branch from an ephemeral branch located on a + remote, e.g., a feature or hotfix branch, then that remote branch + should usually not be used as the upstream branch, since the + push-remote already allows accessing it and having both the + upstream and the push-remote reference the same related branch + would be wasteful. Instead a branch like "maint" or "master" + should be used as the upstream. + + This option allows specifying the branch that should be used as the + upstream when branching certain remote branches. The value is an + alist of the form ‘((UPSTREAM . RULE)...)’. The first matching + element is used, the following elements are ignored. + + UPSTREAM is the branch to be used as the upstream for branches + specified by RULE. It can be a local or a remote branch. + + RULE can either be a regular expression, matching branches whose + upstream should be the one specified by UPSTREAM. Or it can be a + list of the only branches that should *not* use UPSTREAM; all other + branches will. Matching is done after stripping the remote part of + the name of the branch that is being branched from. + + If you use a finite set of non-ephemeral branches across all your + repositories, then you might use something like: + + (("origin/master" . ("master" "next" "maint"))) + + Or if the names of all your ephemeral branches contain a slash, at + least in some repositories, then a good value could be: + + (("origin/master" . "/")) + + Of course you can also fine-tune: + + (("origin/maint" . "\\`hotfix/") + ("origin/master" . "\\`feature/")) + + UPSTREAM can be a local branch: + + (("master" . ("master" "next" "maint"))) + + Because the main branch is no longer almost always named "master" you +should also account for other common names: + + (("main" . ("main" "master" "next" "maint")) + ("master" . ("main" "master" "next" "maint"))) + + -- Command: magit-branch-orphan + This command creates and checks out a new orphan branch with + contents from a given revision. + + -- Command: magit-branch-or-checkout + This command is a hybrid between ‘magit-checkout’ and + ‘magit-branch-and-checkout’ and is intended as a replacement for + the former in ‘magit-branch’. + + It first asks the user for an existing branch or revision. If the + user input actually can be resolved as a branch or revision, then + it checks that out, just like ‘magit-checkout’ would. + + Otherwise it creates and checks out a new branch using the input as + its name. Before doing so it reads the starting-point for the new + branch. This is similar to what ‘magit-branch-and-checkout’ does. + + To use this command instead of ‘magit-checkout’ add this to your + init file: + + (transient-replace-suffix 'magit-branch 'magit-checkout + '("b" "dwim" magit-branch-or-checkout)) + + +File: magit.info, Node: Branch Git Variables, Next: Auxiliary Branch Commands, Prev: Branch Commands, Up: Branching + +6.6.3 Branch Git Variables +-------------------------- + +These variables can be set from the transient prefix command +‘magit-branch-configure’. By default they can also be set from +‘magit-branch’. See *note Branch Commands::. + + -- Variable: branch.NAME.merge + Together with ‘branch.NAME.remote’ this variable defines the + upstream branch of the local branch named NAME. The value of this + variable is the full reference of the upstream _branch_. + + -- Variable: branch.NAME.remote + Together with ‘branch.NAME.merge’ this variable defines the + upstream branch of the local branch named NAME. The value of this + variable is the name of the upstream _remote_. + + -- Variable: branch.NAME.rebase + This variable controls whether pulling into the branch named NAME + is done by rebasing or by merging the fetched branch. + + • When ‘true’ then pulling is done by rebasing. + • When ‘false’ then pulling is done by merging. + • When undefined then the value of ‘pull.rebase’ is used. The + default of that variable is ‘false’. + + -- Variable: branch.NAME.pushRemote + This variable specifies the remote that the branch named NAME is + usually pushed to. The value has to be the name of an existing + remote. + + It is not possible to specify the name of _branch_ to push the + local branch to. The name of the remote branch is always the same + as the name of the local branch. + + If this variable is undefined but ‘remote.pushDefault’ is defined, + then the value of the latter is used. By default + ‘remote.pushDefault’ is undefined. + + -- Variable: branch.NAME.description + This variable can be used to describe the branch named NAME. That + description is used, e.g., when turning the branch into a series of + patches. + + The following variables specify defaults which are used if the above +branch-specific variables are not set. + + -- Variable: pull.rebase + This variable specifies whether pulling is done by rebasing or by + merging. It can be overwritten using ‘branch.NAME.rebase’. + + • When ‘true’ then pulling is done by rebasing. + • When ‘false’ (the default) then pulling is done by merging. + + Since it is never a good idea to merge the upstream branch into a + feature or hotfix branch and most branches are such branches, you + should consider setting this to ‘true’, and ‘branch.master.rebase’ + to ‘false’. + + -- Variable: remote.pushDefault + This variable specifies what remote the local branches are usually + pushed to. This can be overwritten per branch using + ‘branch.NAME.pushRemote’. + + The following variables are used during the creation of a branch and +control whether the various branch-specific variables are automatically +set at this time. + + -- Variable: branch.autoSetupMerge + This variable specifies under what circumstances creating a branch + NAME should result in the variables ‘branch.NAME.merge’ and + ‘branch.NAME.remote’ being set according to the starting point used + to create the branch. If the starting point isn’t a branch, then + these variables are never set. + + • When ‘always’ then the variables are set regardless of whether + the starting point is a local or a remote branch. + • When ‘true’ (the default) then the variables are set when the + starting point is a remote branch, but not when it is a local + branch. + • When ‘false’ then the variables are never set. + + -- Variable: branch.autoSetupRebase + This variable specifies whether creating a branch NAME should + result in the variable ‘branch.NAME.rebase’ being set to ‘true’. + + • When ‘always’ then the variable is set regardless of whether + the starting point is a local or a remote branch. + • When ‘local’ then the variable are set when the starting point + is a local branch, but not when it is a remote branch. + • When ‘remote’ then the variable are set when the starting + point is a remote branch, but not when it is a local branch. + • When ‘never’ (the default) then the variable is never set. + + Note that the respective commands always change the repository-local +values. If you want to change the global value, which is used when the +local value is undefined, then you have to do so on the command line, +e.g.: + + git config --global remote.autoSetupMerge always + + For more information about these variables you should also see *note +(gitman)git-config::. Also see *note (gitman)git-branch::. , *note +(gitman)git-checkout::. and *note Pushing::. + + -- User Option: magit-prefer-remote-upstream + This option controls whether commands that read a branch from the + user and then set it as the upstream branch, offer a local or a + remote branch as default completion candidate, when they have the + choice. + + This affects all commands that use ‘magit-read-upstream-branch’ or + ‘magit-read-starting-point’, which includes all commands that + change the upstream and many which create new branches. + + +File: magit.info, Node: Auxiliary Branch Commands, Prev: Branch Git Variables, Up: Branching + +6.6.4 Auxiliary Branch Commands +------------------------------- + +These commands are not available from the transient ‘magit-branch’ by +default. + + -- Command: magit-branch-shelve + This command shelves a branch. This is done by deleting the + branch, and creating a new reference "refs/shelved/BRANCH-NAME" + pointing at the same commit as the branch pointed at. If the + deleted branch had a reflog, then that is preserved as the reflog + of the new reference. + + This is useful if you want to move a branch out of sight, but are + not ready to completely discard it yet. + + -- Command: magit-branch-unshelve + This command unshelves a branch that was previously shelved using + ‘magit-branch-shelve’. This is done by deleting the reference + "refs/shelved/BRANCH-NAME" and creating a branch "BRANCH-NAME" + pointing at the same commit as the deleted reference pointed at. + If the deleted reference had a reflog, then that is restored as the + reflog of the branch. + + +File: magit.info, Node: Merging, Next: Resolving Conflicts, Prev: Branching, Up: Manipulating + +6.7 Merging +=========== + +Also see *note (gitman)git-merge::. For information on how to resolve +merge conflicts see the next section. + +‘m’ (‘magit-merge’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + When no merge is in progress, then the transient features the +following suffix commands. + +‘m m’ (‘magit-merge-plain’) + This command merges another branch or an arbitrary revision into + the current branch. The branch or revision to be merged is read in + the minibuffer and defaults to the branch at point. + + Unless there are conflicts or a prefix argument is used, then the + resulting merge commit uses a generic commit message, and the user + does not get a chance to inspect or change it before the commit is + created. With a prefix argument this does not actually create the + merge commit, which makes it possible to inspect how conflicts were + resolved and to adjust the commit message. + +‘m e’ (‘magit-merge-editmsg’) + This command merges another branch or an arbitrary revision into + the current branch and opens a commit message buffer, so that the + user can make adjustments. The commit is not actually created + until the user finishes with ‘C-c C-c’. + +‘m n’ (‘magit-merge-nocommit’) + This command merges another branch or an arbitrary revision into + the current branch, but does not actually create the merge commit. + The user can then further adjust the merge, even when automatic + conflict resolution succeeded and/or adjust the commit message. + +‘m a’ (‘magit-merge-absorb’) + This command merges another local branch into the current branch + and then removes the former. + + Before the source branch is merged, it is first force pushed to its + push-remote, provided the respective remote branch already exists. + This ensures that the respective pull-request (if any) won’t get + stuck on some obsolete version of the commits that are being + merged. Finally, if ‘magit-branch-pull-request’ was used to create + the merged branch, then the respective remote branch is also + removed. + +‘m i’ (‘magit-merge-into’) + This command merges the current branch into another local branch + and then removes the former. The latter becomes the new current + branch. + + Before the source branch is merged, it is first force pushed to its + push-remote, provided the respective remote branch already exists. + This ensures that the respective pull-request (if any) won’t get + stuck on some obsolete version of the commits that are being + merged. Finally, if ‘magit-branch-pull-request’ was used to create + the merged branch, then the respective remote branch is also + removed. + +‘m s’ (‘magit-merge-squash’) + This command squashes the changes introduced by another branch or + an arbitrary revision into the current branch. This only applies + the changes made by the squashed commits. No information is + preserved that would allow creating an actual merge commit. + Instead of this command you should probably use a command from the + apply transient. + +‘m p’ (‘magit-merge-preview’) + This command shows a preview of merging another branch or an + arbitrary revision into the current branch. + + Note that commands, that normally change how a diff is displayed, + do not work in buffers created by this command, because the + underlying Git command does not support diff arguments. + + When a merge is in progress, then the transient instead features the +following suffix commands. + +‘m m’ (‘magit-merge’) + After the user resolved conflicts, this command proceeds with the + merge. If some conflicts weren’t resolved, then this command + fails. + +‘m a’ (‘magit-merge-abort’) + This command aborts the current merge operation. + + +File: magit.info, Node: Resolving Conflicts, Next: Rebasing, Prev: Merging, Up: Manipulating + +6.8 Resolving Conflicts +======================= + +When merging branches (or otherwise combining or changing history) +conflicts can occur. If you edited two completely different parts of +the same file in two branches and then merge one of these branches into +the other, then Git can resolve that on its own, but if you edit the +same area of a file, then a human is required to decide how the two +versions, or "sides of the conflict", are to be combined into one. + + Here we can only provide a brief introduction to the subject and +point you toward some tools that can help. If you are new to this, then +please also consult Git’s own documentation as well as other resources. + + If a file has conflicts and Git cannot resolve them by itself, then +it puts both versions into the affected file along with special markers +whose purpose is to denote the boundaries of the unresolved part of the +file and between the different versions. These boundary lines begin +with the strings consisting of seven times the same character, one of +‘<’, ‘|’, ‘=’ and ‘>’, and are followed by information about the source +of the respective versions, e.g.: + + <<<<<<< HEAD + Take the blue pill. + ======= + Take the red pill. + >>>>>>> feature + + In this case you have chosen to take the red pill on one branch and +on another you picked the blue pill. Now that you are merging these two +diverging branches, Git cannot possibly know which pill you want to +take. + + To resolve that conflict you have to create a version of the affected +area of the file by keeping only one of the sides, possibly by editing +it in order to bring in the changes from the other side, remove the +other versions as well as the markers, and then stage the result. A +possible resolution might be: + + Take both pills. + + Often it is useful to see not only the two sides of the conflict but +also the "original" version from before the same area of the file was +modified twice on different branches. Instruct Git to insert that +version as well by running this command once: + + git config --global merge.conflictStyle diff3 + + The above conflict might then have looked like this: + + <<<<<<< HEAD + Take the blue pill. + ||||||| merged common ancestors + Take either the blue or the red pill, but not both. + ======= + Take the red pill. + >>>>>>> feature + + If that were the case, then the above conflict resolution would not +have been correct, which demonstrates why seeing the original version +alongside the conflicting versions can be useful. + + You can perform the conflict resolution completely by hand, but Emacs +also provides some packages that help in the process: Smerge, Ediff +(*note (ediff)Top::), and Emerge (*note (emacs)Emerge::). Magit does +not provide its own tools for conflict resolution, but it does make +using Smerge and Ediff more convenient. (Ediff supersedes Emerge, so +you probably don’t want to use the latter anyway.) + + In the Magit status buffer, files with unresolved conflicts are +listed in the "Unstaged changes" and/or "Staged changes" sections. They +are prefixed with the word "unmerged", which in this context essentially +is a synonym for "unresolved". + + Pressing ‘RET’ while point is on such a file section shows a buffer +visiting that file, turns on ‘smerge-mode’ in that buffer, and places +point inside the first area with conflicts. You should then resolve +that conflict using regular edit commands and/or Smerge commands. + + Unfortunately Smerge does not have a manual, but you can get a list +of commands and binding ‘C-c ^ C-h’ and press ‘RET’ while point is on a +command name to read its documentation. + + Normally you would edit one version and then tell Smerge to keep only +that version. Use ‘C-c ^ m’ (‘smerge-keep-mine’) to keep the ‘HEAD’ +version or ‘C-c ^ o’ (‘smerge-keep-other’) to keep the version that +follows "|||||||". Then use ‘C-c ^ n’ to move to the next conflicting +area in the same file. Once you are done resolving conflicts, return to +the Magit status buffer. The file should now be shown as "modified", no +longer as "unmerged", because Smerge automatically stages the file when +you save the buffer after resolving the last conflict. + + Magit now wraps the mentioned Smerge commands, allowing you to use +these key bindings without having to go to the file-visiting buffer. +Additionally ‘k’ (‘magit-discard’) on a hunk with unresolved conflicts +asks which side to keep or, if point is on a side, then it keeps it +without prompting. Similarly ‘k’ on a unresolved file ask which side to +keep. + + Alternatively you could use Ediff, which uses separate buffers for +the different versions of the file. To resolve conflicts in a file +using Ediff press ‘e’ while point is on such a file in the status +buffer. + + Ediff can be used for other purposes as well. For more information +on how to enter Ediff from Magit, see *note Ediffing::. Explaining how +to use Ediff is beyond the scope of this manual, instead see *note +(ediff)Top::. + + If you are unsure whether you should Smerge or Ediff, then use the +former. It is much easier to understand and use, and except for truly +complex conflicts, the latter is usually overkill. + + +File: magit.info, Node: Rebasing, Next: Cherry Picking, Prev: Resolving Conflicts, Up: Manipulating + +6.9 Rebasing +============ + +Also see *note (gitman)git-rebase::. For information on how to resolve +conflicts that occur during rebases see the preceding section. + +‘r’ (‘magit-rebase’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + When no rebase is in progress, then the transient features the +following suffix commands. + + Using one of these commands _starts_ a rebase sequence. Git might +then stop somewhere along the way, either because you told it to do so, +or because applying a commit failed due to a conflict. When that +happens, then the status buffer shows information about the rebase +sequence which is in progress in a section similar to a log section. +See *note Information About In-Progress Rebase::. + + For information about the upstream and the push-remote, see *note The +Two Remotes::. + +‘r p’ (‘magit-rebase-onto-pushremote’) + This command rebases the current branch onto its push-remote. + + With a prefix argument or when the push-remote is either not + configured or unusable, then let the user first configure the + push-remote. + +‘r u’ (‘magit-rebase-onto-upstream’) + This command rebases the current branch onto its upstream branch. + + With a prefix argument or when the upstream is either not + configured or unusable, then let the user first configure the + upstream. + +‘r e’ (‘magit-rebase-branch’) + This command rebases the current branch onto a branch read in the + minibuffer. All commits that are reachable from head but not from + the selected branch TARGET are being rebased. + +‘r s’ (‘magit-rebase-subset’) + This command starts a non-interactive rebase sequence to transfer + commits from START to ‘HEAD’ onto NEWBASE. START has to be + selected from a list of recent commits. + + By default Magit uses the ‘--autostash’ argument, which causes +uncommitted changes to be stored in a stash before the rebase begins. +These changes are restored after the rebase completes and if possible +the stash is removed. If the stash does not apply cleanly, then the +stash is not removed. In case something goes wrong when resolving the +conflicts, this allows you to start over. + + Even though one of the actions is dedicated to interactive rebases, +the transient also features the infix argument ‘--interactive’. This +can be used to turn one of the other, non-interactive rebase variants +into an interactive rebase. + + For example if you want to clean up a feature branch and at the same +time rebase it onto ‘master’, then you could use ‘r-iu’. But we +recommend that you instead do that in two steps. First use ‘ri’ to +cleanup the feature branch, and then in a second step ‘ru’ to rebase it +onto ‘master’. That way if things turn out to be more complicated than +you thought and/or you make a mistake and have to start over, then you +only have to redo half the work. + + Explicitly enabling ‘--interactive’ won’t have an effect on the +following commands as they always use that argument anyway, even if it +is not enabled in the transient. + +‘r i’ (‘magit-rebase-interactive’) + This command starts an interactive rebase sequence. + +‘r f’ (‘magit-rebase-autosquash’) + This command combines squash and fixup commits with their intended + targets. + +‘r m’ (‘magit-rebase-edit-commit’) + This command starts an interactive rebase sequence that lets the + user edit a single older commit. + +‘r w’ (‘magit-rebase-reword-commit’) + This command starts an interactive rebase sequence that lets the + user reword a single older commit. + +‘r k’ (‘magit-rebase-remove-commit’) + This command removes a single older commit using rebase. + + When a rebase is in progress, then the transient instead features the +following suffix commands. + +‘r r’ (‘magit-rebase-continue’) + This command restart the current rebasing operation. + + In some cases this pops up a commit message buffer for you do edit. + With a prefix argument the old message is reused as-is. + +‘r s’ (‘magit-rebase-skip’) + This command skips the current commit and restarts the current + rebase operation. + +‘r e’ (‘magit-rebase-edit’) + This command lets the user edit the todo list of the current rebase + operation. + +‘r a’ (‘magit-rebase-abort’) + This command aborts the current rebase operation, restoring the + original branch. + +* Menu: + +* Editing Rebase Sequences:: +* Information About In-Progress Rebase:: + + +File: magit.info, Node: Editing Rebase Sequences, Next: Information About In-Progress Rebase, Up: Rebasing + +6.9.1 Editing Rebase Sequences +------------------------------ + +‘C-c C-c’ (‘with-editor-finish’) + Finish the current editing session by returning with exit code 0. + Git then uses the rebase instructions it finds in the file. + +‘C-c C-k’ (‘with-editor-cancel’) + Cancel the current editing session by returning with exit code 1. + Git then forgoes starting the rebase sequence. + +‘<RET>’ (‘git-rebase-show-commit’) + Show the commit on the current line in another buffer and select + that buffer. + +‘<SPC>’ (‘git-rebase-show-or-scroll-up’) + Show the commit on the current line in another buffer without + selecting that buffer. If the revision buffer is already visible + in another window of the current frame, then instead scroll that + window up. + +‘<DEL>’ (‘git-rebase-show-or-scroll-down’) + Show the commit on the current line in another buffer without + selecting that buffer. If the revision buffer is already visible + in another window of the current frame, then instead scroll that + window down. + +‘p’ (‘git-rebase-backward-line’) + Move to previous line. + +‘n’ (‘forward-line’) + Move to next line. + +‘M-p’ (‘git-rebase-move-line-up’) + Move the current commit (or command) up. + +‘M-n’ (‘git-rebase-move-line-down’) + Move the current commit (or command) down. + +‘r’ (‘git-rebase-reword’) + Edit message of commit on current line. + +‘e’ (‘git-rebase-edit’) + Stop at the commit on the current line. + +‘s’ (‘git-rebase-squash’) + Meld commit on current line into previous commit, and edit message. + +‘f’ (‘git-rebase-fixup’) + Meld commit on current line into previous commit, discarding the + current commit’s message. + +‘k’ (‘git-rebase-kill-line’) + Kill the current action line. + +‘c’ (‘git-rebase-pick’) + Use commit on current line. + +‘x’ (‘git-rebase-exec’) + Insert a shell command to be run after the proceeding commit. + + If there already is such a command on the current line, then edit + that instead. With a prefix argument insert a new command even + when there already is one on the current line. With empty input + remove the command on the current line, if any. + +‘b’ (‘git-rebase-break’) + Insert a break action before the current line, instructing Git to + return control to the user. + +‘y’ (‘git-rebase-insert’) + Read an arbitrary commit and insert it below current line. + +‘C-x u’ (‘git-rebase-undo’) + Undo some previous changes. Like ‘undo’ but works in read-only + buffers. + + -- User Option: git-rebase-auto-advance + Whether to move to next line after changing a line. + + -- User Option: git-rebase-show-instructions + Whether to show usage instructions inside the rebase buffer. + + -- User Option: git-rebase-confirm-cancel + Whether confirmation is required to cancel. + + When a rebase is performed with the ‘--rebase-merges’ option, the +sequence will include a few other types of actions and the following +commands become relevant. + +‘l’ (‘git-rebase-label’) + This commands inserts a label action or edits the one at point. + +‘t’ (‘git-rebase-reset’) + This command inserts a reset action or edits the one at point. The + prompt will offer the labels that are currently present in the + buffer. + +‘MM’ (‘git-rebase-merge’) + The command inserts a merge action or edits the one at point. The + prompt will offer the labels that are currently present in the + buffer. Specifying a message to reuse via ‘-c’ or ‘-C’ is not + supported; an editor will always be invoked for the merge. + +‘Mt’ (‘git-rebase-merge-toggle-editmsg’) + This command toggles between the ‘-C’ and ‘-c’ options of the merge + action at point. These options both specify a commit whose message + should be reused. The lower-case variant instructs Git to invoke + the editor when creating the merge, allowing the user to edit the + message. + + +File: magit.info, Node: Information About In-Progress Rebase, Prev: Editing Rebase Sequences, Up: Rebasing + +6.9.2 Information About In-Progress Rebase +------------------------------------------ + +While a rebase sequence is in progress, the status buffer features a +section that lists the commits that have already been applied as well as +the commits that still have to be applied. + + The commits are split in two halves. When rebase stops at a commit, +either because the user has to deal with a conflict or because s/he +explicitly requested that rebase stops at that commit, then point is +placed on the commit that separates the two groups, i.e., on ‘HEAD’. +The commits above it have not been applied yet, while the ‘HEAD’ and the +commits below it have already been applied. In between these two groups +of applied and yet-to-be applied commits, there sometimes is a commit +which has been dropped. + + Each commit is prefixed with a word and these words are additionally +shown in different colors to indicate the status of the commits. + + The following colors are used: + + • Commits that use the same foreground color as the ‘default’ face + have not been applied yet. + + • Yellow commits have some special relationship to the commit rebase + stopped at. This is used for the words "join", "goal", "same" and + "work" (see below). + + • Gray commits have already been applied. + + • The blue commit is the ‘HEAD’ commit. + + • The green commit is the commit the rebase sequence stopped at. If + this is the same commit as ‘HEAD’ (e.g., because you haven’t done + anything yet after rebase stopped at the commit, then this commit + is shown in blue, not green). There can only be a green *and* a + blue commit at the same time, if you create one or more new commits + after rebase stops at a commit. + + • Red commits have been dropped. They are shown for reference only, + e.g., to make it easier to diff. + + Of course these colors are subject to the color-theme in use. + + The following words are used: + + • Commits prefixed with ‘pick’, ‘reword’, ‘edit’, ‘squash’, and + ‘fixup’ have not been applied yet. These words have the same + meaning here as they do in the buffer used to edit the rebase + sequence. See *note Editing Rebase Sequences::. When the + ‘--rebase-merges’ option was specified, ‘reset’, ‘label’, and + ‘merge’ lines may also be present. + + • Commits prefixed with ‘done’ and ‘onto’ have already been applied. + It is possible for such a commit to be the ‘HEAD’, in which case it + is blue. Otherwise it is grey. + + • The commit prefixed with ‘onto’ is the commit on top of which + all the other commits are being re-applied. This commit + itself did not have to be re-applied, it is the commit rebase + did rewind to before starting to re-apply other commits. + + • Commits prefixed with ‘done’ have already been re-applied. + This includes commits that have been re-applied but also new + commits that you have created during the rebase. + + • All other commits, those not prefixed with any of the above words, + are in some way related to the commit at which rebase stopped. + + To determine whether a commit is related to the stopped-at commit + their hashes, trees and patch-ids (1) are being compared. The + commit message is not used for this purpose. + + Generally speaking commits that are related to the stopped-at + commit can have any of the used colors, though not all color/word + combinations are possible. + + Words used for stopped-at commits are: + + • When a commit is prefixed with ‘void’, then that indicates + that Magit knows for sure that all the changes in that commit + have been applied using several new commits. This commit is + no longer reachable from ‘HEAD’, and it also isn’t one of the + commits that will be applied when resuming the session. + + • When a commit is prefixed with ‘join’, then that indicates + that the rebase sequence stopped at that commit due to a + conflict - you now have to join (merge) the changes with what + has already been applied. In a sense this is the commit + rebase stopped at, but while its effect is already in the + index and in the worktree (with conflict markers), the commit + itself has not actually been applied yet (it isn’t the + ‘HEAD’). So it is shown in yellow, like the other commits + that still have to be applied. + + • When a commit is prefixed with ‘stop’ or a _blue_ or _green_ + ‘same’, then that indicates that rebase stopped at this + commit, that it is still applied or has been applied again, + and that at least its patch-id is unchanged. + + • When a commit is prefixed with ‘stop’, then that + indicates that rebase stopped at that commit because you + requested that earlier, and its patch-id is unchanged. + It might even still be the exact same commit. + + • When a commit is prefixed with a _blue_ or _green_ + ‘same’, then that indicates that while its tree or hash + changed, its patch-id did not. If it is blue, then it is + the ‘HEAD’ commit (as always for blue). When it is + green, then it no longer is ‘HEAD’ because other commit + have been created since (but before continuing the + rebase). + + • When a commit is prefixed with ‘goal’, a _yellow_ ‘same,’ or + ‘work’, then that indicates that rebase applied that commit + but that you then reset ‘HEAD’ to an earlier commit (likely to + split it up into multiple commits), and that there are some + uncommitted changes remaining which likely (but not + necessarily) originate from that commit. + + • When a commit is prefixed with ‘goal’, then that + indicates that it is still possible to create a new + commit with the exact same tree (the "goal") without + manually editing any files, by committing the index, or + by staging all changes and then committing that. This is + the case when the original tree still exists in the index + or worktree in untainted form. + + • When a commit is prefixed with a yellow ‘same’, then that + indicates that it is no longer possible to create a + commit with the exact same tree, but that it is still + possible to create a commit with the same patch-id. This + would be the case if you created a new commit with other + changes, but the changes from the original commit still + exist in the index or working tree in untainted form. + + • When a commit is prefixed with ‘work’, then that + indicates that you reset ‘HEAD’ to an earlier commit, and + that there are some staged and/or unstaged changes + (likely, but not necessarily) originating from that + commit. However it is no longer possible to create a new + commit with the same tree or at least the same patch-id + because you have already made other changes. + + • When a commit is prefixed with ‘poof’ or ‘gone’, then that + indicates that rebase applied that commit but that you then + reset ‘HEAD’ to an earlier commit (likely to split it up into + multiple commits), and that there are no uncommitted changes. + + • When a commit is prefixed with ‘poof’, then that + indicates that it is no longer reachable from ‘HEAD’, but + that it has been replaced with one or more commits, which + together have the exact same effect. + + • When a commit is prefixed with ‘gone’, then that + indicates that it is no longer reachable from ‘HEAD’ and + that we also cannot determine whether its changes are + still in effect in one or more new commits. They might + be, but if so, then there must also be other changes + which makes it impossible to know for sure. + + Do not worry if you do not fully understand the above. That’s okay, +you will acquire a good enough understanding through practice. + + For other sequence operations such as cherry-picking, a similar +section is displayed, but they lack some of the features described +above, due to limitations in the git commands used to implement them. +Most importantly these sequences only support "picking" a commit but not +other actions such as "rewording", and they do not keep track of the +commits which have already been applied. + + ---------- Footnotes ---------- + + (1) The patch-id is a hash of the _changes_ introduced by a commit. +It differs from the hash of the commit itself, which is a hash of the +result of applying that change (i.e., the resulting trees and blobs) as +well as author and committer information, the commit message, and the +hashes of the parents of the commit. The patch-id hash on the other +hand is created only from the added and removed lines, even line numbers +and whitespace changes are ignored when calculating this hash. The +patch-ids of two commits can be used to answer the question "Do these +commits make the same change?". + + +File: magit.info, Node: Cherry Picking, Next: Resetting, Prev: Rebasing, Up: Manipulating + +6.10 Cherry Picking +=================== + +Also see *note (gitman)git-cherry-pick::. + +‘A’ (‘magit-cherry-pick’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + When no cherry-pick or revert is in progress, then the transient +features the following suffix commands. + +‘A A’ (‘magit-cherry-copy’) + This command copies COMMITS from another branch onto the current + branch. If the region selects multiple commits, then those are + copied, without prompting. Otherwise the user is prompted for a + commit or range, defaulting to the commit at point. + +‘A a’ (‘magit-cherry-apply’) + This command applies the changes in COMMITS from another branch + onto the current branch. If the region selects multiple commits, + then those are used, without prompting. Otherwise the user is + prompted for a commit or range, defaulting to the commit at point. + + This command also has a top-level binding, which can be invoked + without using the transient by typing ‘a’ at the top-level. + + The following commands not only apply some commits to some branch, +but also remove them from some other branch. The removal is performed +using either ‘git-update-ref’ or if necessary ‘git-rebase’. Both +applying commits as well as removing them using ‘git-rebase’ can lead to +conflicts. If that happens, then these commands abort and you not only +have to resolve the conflicts but also finish the process the same way +you would have to if these commands didn’t exist at all. + +‘A h’ (‘magit-cherry-harvest’) + This command moves the selected COMMITS that must be located on + another BRANCH onto the current branch instead, removing them from + the former. When this command succeeds, then the same branch is + current as before. + + Applying the commits on the current branch or removing them from + the other branch can lead to conflicts. When that happens, then + this command stops and you have to resolve the conflicts and then + finish the process manually. + +‘A d’ (‘magit-cherry-donate’) + This command moves the selected COMMITS from the current branch + onto another existing BRANCH, removing them from the former. When + this command succeeds, then the same branch is current as before. + ‘HEAD’ is allowed to be detached initially. + + Applying the commits on the other branch or removing them from the + current branch can lead to conflicts. When that happens, then this + command stops and you have to resolve the conflicts and then finish + the process manually. + +‘A n’ (‘magit-cherry-spinout’) + This command moves the selected COMMITS from the current branch + onto a new branch BRANCH, removing them from the former. When this + command succeeds, then the same branch is current as before. + + Applying the commits on the other branch or removing them from the + current branch can lead to conflicts. When that happens, then this + command stops and you have to resolve the conflicts and then finish + the process manually. + +‘A s’ (‘magit-cherry-spinoff’) + This command moves the selected COMMITS from the current branch + onto a new branch BRANCH, removing them from the former. When this + command succeeds, then the new branch is checked out. + + Applying the commits on the other branch or removing them from the + current branch can lead to conflicts. When that happens, then this + command stops and you have to resolve the conflicts and then finish + the process manually. + + When a cherry-pick or revert is in progress, then the transient +instead features the following suffix commands. + +‘A A’ (‘magit-sequence-continue’) + Resume the current cherry-pick or revert sequence. + +‘A s’ (‘magit-sequence-skip’) + Skip the stopped at commit during a cherry-pick or revert sequence. + +‘A a’ (‘magit-sequence-abort’) + Abort the current cherry-pick or revert sequence. This discards + all changes made since the sequence started. + +* Menu: + +* Reverting:: + + +File: magit.info, Node: Reverting, Up: Cherry Picking + +6.10.1 Reverting +---------------- + +‘V’ (‘magit-revert’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + When no cherry-pick or revert is in progress, then the transient +features the following suffix commands. + +‘V V’ (‘magit-revert-and-commit’) + Revert a commit by creating a new commit. Prompt for a commit, + defaulting to the commit at point. If the region selects multiple + commits, then revert all of them, without prompting. + +‘V v’ (‘magit-revert-no-commit’) + Revert a commit by applying it in reverse to the working tree. + Prompt for a commit, defaulting to the commit at point. If the + region selects multiple commits, then revert all of them, without + prompting. + + When a cherry-pick or revert is in progress, then the transient +instead features the following suffix commands. + +‘V V’ (‘magit-sequence-continue’) + Resume the current cherry-pick or revert sequence. + +‘V s’ (‘magit-sequence-skip’) + Skip the stopped at commit during a cherry-pick or revert sequence. + +‘V a’ (‘magit-sequence-abort’) + Abort the current cherry-pick or revert sequence. This discards + all changes made since the sequence started. + + +File: magit.info, Node: Resetting, Next: Stashing, Prev: Cherry Picking, Up: Manipulating + +6.11 Resetting +============== + +Also see *note (gitman)git-reset::. + +‘x’ (‘magit-reset-quickly’) + Reset the ‘HEAD’ and index to some commit read from the user and + defaulting to the commit at point, and possibly also reset the + working tree. With a prefix argument reset the working tree + otherwise don’t. + +‘X m’ (‘magit-reset-mixed’) + Reset the ‘HEAD’ and index to some commit read from the user and + defaulting to the commit at point. The working tree is kept as-is. + +‘X s’ (‘magit-reset-soft’) + Reset the ‘HEAD’ to some commit read from the user and defaulting + to the commit at point. The index and the working tree are kept + as-is. + +‘X h’ (‘magit-reset-hard’) + Reset the ‘HEAD’, index, and working tree to some commit read from + the user and defaulting to the commit at point. + +‘X k’ (‘magit-reset-keep’) + Reset the ‘HEAD’, index, and working tree to some commit read from + the user and defaulting to the commit at point. Uncommitted + changes are kept as-is. + +‘X i’ (‘magit-reset-index’) + Reset the index to some commit read from the user and defaulting to + the commit at point. Keep the ‘HEAD’ and working tree as-is, so if + the commit refers to the ‘HEAD’, then this effectively unstages all + changes. + +‘X w’ (‘magit-reset-worktree’) + Reset the working tree to some commit read from the user and + defaulting to the commit at point. Keep the ‘HEAD’ and index + as-is. + +‘X f’ (‘magit-file-checkout’) + Update file in the working tree and index to the contents from a + revision. Both the revision and file are read from the user. + + +File: magit.info, Node: Stashing, Prev: Resetting, Up: Manipulating + +6.12 Stashing +============= + +Also see *note (gitman)git-stash::. + +‘z’ (‘magit-stash’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘z z’ (‘magit-stash-both’) + Create a stash of the index and working tree. Untracked files are + included according to infix arguments. One prefix argument is + equivalent to ‘--include-untracked’ while two prefix arguments are + equivalent to ‘--all’. + +‘z i’ (‘magit-stash-index’) + Create a stash of the index only. Unstaged and untracked changes + are not stashed. + +‘z w’ (‘magit-stash-worktree’) + Create a stash of unstaged changes in the working tree. Untracked + files are included according to infix arguments. One prefix + argument is equivalent to ‘--include-untracked’ while two prefix + arguments are equivalent to ‘--all’. + +‘z x’ (‘magit-stash-keep-index’) + Create a stash of the index and working tree, keeping index intact. + Untracked files are included according to infix arguments. One + prefix argument is equivalent to ‘--include-untracked’ while two + prefix arguments are equivalent to ‘--all’. + +‘z Z’ (‘magit-snapshot-both’) + Create a snapshot of the index and working tree. Untracked files + are included according to infix arguments. One prefix argument is + equivalent to ‘--include-untracked’ while two prefix arguments are + equivalent to ‘--all’. + +‘z I’ (‘magit-snapshot-index’) + Create a snapshot of the index only. Unstaged and untracked + changes are not stashed. + +‘z W’ (‘magit-snapshot-worktree’) + Create a snapshot of unstaged changes in the working tree. + Untracked files are included according to infix arguments. One + prefix argument is equivalent to ‘--include-untracked’ while two + prefix arguments are equivalent to ‘--all’-. + +‘z a’ (‘magit-stash-apply’) + Apply a stash to the working tree. + + First try ‘git stash apply --index’, which tries to preserve the + index stored in the stash, if any. This may fail because applying + the stash could result in conflicts and those have to be stored in + the index, making it impossible to also store the stash’s index + there as well. + + If the above failed, then try ‘git stash apply’. This fails (with + or without ‘--index’) if there are any uncommitted changes to files + that are also modified in the stash. + + If both of the above failed, then apply using ‘git apply’. If + there are no conflicting files, use ‘--3way’. If there are + conflicting files, then using ‘--3way’ requires that those files + are staged first, which may be undesirable, so prompt the user + whether to use ‘--3way’ or ‘--reject’. + + Customize ‘magit-no-confirm’ if you want to always use ‘--3way’, + without being prompted. + +‘z p’ (‘magit-stash-pop’) + Apply a stash to the working tree. On complete success (if the + stash can be applied without any conflicts, and while preserving + the stash’s index) then remove the stash from stash list. + + First try ‘git stash pop --index’, which tries to preserve the + index stored in the stash, if any. This may fail because applying + the stash could result in conflicts and those have to be stored in + the index, making it impossible to also store the stash’s index + there as well. + + If the above failed, then try ‘git stash apply’. This fails (with + or without ‘--index’) if there are any uncommitted changes to files + that are also modified in the stash. + + If both of the above failed, then apply using ‘git apply’. If + there are no conflicting files, use ‘--3way’. If there are + conflicting files, then using ‘--3way’ requires that those files + are staged first, which may be undesirable, so prompt the user + whether to use ‘--3way’ or ‘--reject’. + + Customize ‘magit-no-confirm’ if you want to always use ‘--3way’, + without being prompted. + +‘z k’ (‘magit-stash-drop’) + Remove a stash from the stash list. When the region is active, + offer to drop all contained stashes. + +‘z v’ (‘magit-stash-show’) + Show all diffs of a stash in a buffer. + +‘z b’ (‘magit-stash-branch’) + Create and checkout a new branch from an existing stash. The new + branch starts at the commit that was current when the stash was + created. + +‘z B’ (‘magit-stash-branch-here’) + Create and checkout a new branch from an existing stash. Use the + current branch or ‘HEAD’ as the starting-point of the new branch. + Then apply the stash, dropping it if it applies cleanly. + +‘z f’ (‘magit-stash-format-patch’) + Create a patch from STASH. + +‘k’ (‘magit-stash-clear’) + Remove all stashes saved in REF’s reflog by deleting REF. + +‘z l’ (‘magit-stash-list’) + List all stashes in a buffer. + + -- User Option: magit-stashes-margin + This option specifies whether the margin is initially shown in + stashes buffers and how it is formatted. + + The value has the form ‘(INIT STYLE WIDTH AUTHOR AUTHOR-WIDTH)’. + + • If INIT is non-nil, then the margin is shown initially. + • STYLE controls how to format the author or committer date. It + can be one of ‘age’ (to show the age of the commit), + ‘age-abbreviated’ (to abbreviate the time unit to a + character), or a string (suitable for ‘format-time-string’) to + show the actual date. Option + ‘magit-log-margin-show-committer-date’ controls which date is + being displayed. + • WIDTH controls the width of the margin. This exists for + forward compatibility and currently the value should not be + changed. + • AUTHOR controls whether the name of the author is also shown + by default. + • AUTHOR-WIDTH has to be an integer. When the name of the + author is shown, then this specifies how much space is used to + do so. + + +File: magit.info, Node: Transferring, Next: Miscellaneous, Prev: Manipulating, Up: Top + +7 Transferring +************** + +* Menu: + +* Remotes:: +* Fetching:: +* Pulling:: +* Pushing:: +* Plain Patches:: +* Maildir Patches:: + + +File: magit.info, Node: Remotes, Next: Fetching, Up: Transferring + +7.1 Remotes +=========== + +* Menu: + +* Remote Commands:: +* Remote Git Variables:: + + +File: magit.info, Node: Remote Commands, Next: Remote Git Variables, Up: Remotes + +7.1.1 Remote Commands +--------------------- + +The transient prefix command ‘magit-remote’ is used to add remotes and +to make changes to existing remotes. This command only deals with +remotes themselves, not with branches or the transfer of commits. Those +features are available from separate transient commands. + + Also see *note (gitman)git-remote::. + +‘M’ (‘magit-remote’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + + By default it also binds and displays the values of some + remote-related Git variables and allows changing their values. + + -- User Option: magit-remote-direct-configure + This option controls whether remote-related Git variables are + accessible directly from the transient ‘magit-remote’. + + If ‘t’ (the default) and a local branch is checked out, then + ‘magit-remote’ features the variables for the upstream remote of + that branch, or if ‘HEAD’ is detached, for ‘origin’, provided that + exists. + + If ‘nil’, then ‘magit-remote-configure’ has to be used to do so. + +‘M C’ (‘magit-remote-configure’) + This transient prefix command binds commands that set the value of + remote-related variables and displays them in a temporary buffer + until the transient is exited. + + With a prefix argument, this command always prompts for a remote. + + Without a prefix argument this depends on whether it was invoked as + a suffix of ‘magit-remote’ and on the + ‘magit-remote-direct-configure’ option. If ‘magit-remote’ already + displays the variables for the upstream, then it does not make + sense to invoke another transient that displays them for the same + remote. In that case this command prompts for a remote. + + The variables are described in *note Remote Git Variables::. + +‘M a’ (‘magit-remote-add’) + This command add a remote and fetches it. The remote name and url + are read in the minibuffer. + +‘M r’ (‘magit-remote-rename’) + This command renames a remote. Both the old and the new names are + read in the minibuffer. + +‘M u’ (‘magit-remote-set-url’) + This command changes the url of a remote. Both the remote and the + new url are read in the minibuffer. + +‘M k’ (‘magit-remote-remove’) + This command deletes a remote, read in the minibuffer. + +‘M p’ (‘magit-remote-prune’) + This command removes stale remote-tracking branches for a remote + read in the minibuffer. + +‘M P’ (‘magit-remote-prune-refspecs’) + This command removes stale refspecs for a remote read in the + minibuffer. + + A refspec is stale if there no longer exists at least one branch on + the remote that would be fetched due to that refspec. A stale + refspec is problematic because its existence causes Git to refuse + to fetch according to the remaining non-stale refspecs. + + If only stale refspecs remain, then this command offers to either + delete the remote or to replace the stale refspecs with the default + refspec ("+refs/heads/*:refs/remotes/REMOTE/*"). + + This command also removes the remote-tracking branches that were + created due to the now stale refspecs. Other stale branches are + not removed. + + -- User Option: magit-remote-add-set-remote.pushDefault + This option controls whether the user is asked whether they want to + set ‘remote.pushDefault’ after adding a remote. + + If ‘ask’, then users is always ask. If ‘ask-if-unset’, then the + user is only if the variable isn’t set already. If ‘nil’, then the + user isn’t asked and the variable isn’t set. If the value is a + string, then the variable is set without the user being asked, + provided that the name of the added remote is equal to that string + and the variable isn’t already set. + + +File: magit.info, Node: Remote Git Variables, Prev: Remote Commands, Up: Remotes + +7.1.2 Remote Git Variables +-------------------------- + +These variables can be set from the transient prefix command +‘magit-remote-configure’. By default they can also be set from +‘magit-remote’. See *note Remote Commands::. + + -- Variable: remote.NAME.url + This variable specifies the url of the remote named NAME. It can + have multiple values. + + -- Variable: remote.NAME.fetch + The refspec used when fetching from the remote named NAME. It can + have multiple values. + + -- Variable: remote.NAME.pushurl + This variable specifies the url used for pushing to the remote + named NAME. If it is not specified, then ‘remote.NAME.url’ is used + instead. It can have multiple values. + + -- Variable: remote.NAME.push + The refspec used when pushing to the remote named NAME. It can + have multiple values. + + -- Variable: remote.NAME.tagOpts + This variable specifies what tags are fetched by default. If the + value is ‘--no-tags’ then no tags are fetched. If the value is + ‘--tags’, then all tags are fetched. If this variable has no + value, then only tags are fetched that are reachable from fetched + branches. + + +File: magit.info, Node: Fetching, Next: Pulling, Prev: Remotes, Up: Transferring + +7.2 Fetching +============ + +Also see *note (gitman)git-fetch::. For information about the upstream +and the push-remote, see *note The Two Remotes::. + +‘f’ (‘magit-fetch’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘f p’ (‘magit-fetch-from-pushremote’) + This command fetches from the current push-remote. + + With a prefix argument or when the push-remote is either not + configured or unusable, then let the user first configure the + push-remote. + +‘f u’ (‘magit-fetch-from-upstream’) + This command fetch from the upstream of the current branch. + + If the upstream is configured for the current branch and names an + existing remote, then use that. Otherwise try to use another + remote: If only a single remote is configured, then use that. + Otherwise if a remote named "origin" exists, then use that. + + If no remote can be determined, then this command is not available + from the ‘magit-fetch’ transient prefix and invoking it directly + results in an error. + +‘f e’ (‘magit-fetch-other’) + This command fetch from a repository read from the minibuffer. + +‘f o’ (‘magit-fetch-branch’) + This command fetches a branch from a remote, both of which are read + from the minibuffer. + +‘f r’ (‘magit-fetch-refspec’) + This command fetches from a remote using an explicit refspec, both + of which are read from the minibuffer. + +‘f a’ (‘magit-fetch-all’) + This command fetches from all remotes. + +‘f m’ (‘magit-fetch-modules’) + This command fetches all submodules. With a prefix argument, it + acts as a transient prefix command, allowing the caller to set + options. + + -- User Option: magit-pull-or-fetch + By default fetch and pull commands are available from separate + transient prefix command. Setting this to ‘t’ adds some (but not + all) of the above suffix commands to the ‘magit-pull’ transient. + + If you do that, then you might also want to change the key binding + for these prefix commands, e.g.: + + (setq magit-pull-or-fetch t) + (define-key magit-mode-map "f" 'magit-pull) ; was magit-fetch + (define-key magit-mode-map "F" nil) ; was magit-pull + + +File: magit.info, Node: Pulling, Next: Pushing, Prev: Fetching, Up: Transferring + +7.3 Pulling +=========== + +Also see *note (gitman)git-pull::. For information about the upstream +and the push-remote, see *note The Two Remotes::. + +‘F’ (‘magit-pull’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + +‘F p’ (‘magit-pull-from-pushremote’) + This command pulls from the push-remote of the current branch. + + With a prefix argument or when the push-remote is either not + configured or unusable, then let the user first configure the + push-remote. + +‘F u’ (‘magit-pull-from-upstream’) + This command pulls from the upstream of the current branch. + + With a prefix argument or when the upstream is either not + configured or unusable, then let the user first configure the + upstream. + +‘F e’ (‘magit-pull-branch’) + This command pulls from a branch read in the minibuffer. + + +File: magit.info, Node: Pushing, Next: Plain Patches, Prev: Pulling, Up: Transferring + +7.4 Pushing +=========== + +Also see *note (gitman)git-push::. For information about the upstream +and the push-remote, see *note The Two Remotes::. + +‘P’ (‘magit-push’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘P p’ (‘magit-push-current-to-pushremote’) + This command pushes the current branch to its push-remote. + + With a prefix argument or when the push-remote is either not + configured or unusable, then let the user first configure the + push-remote. + +‘P u’ (‘magit-push-current-to-upstream’) + This command pushes the current branch to its upstream branch. + + With a prefix argument or when the upstream is either not + configured or unusable, then let the user first configure the + upstream. + +‘P e’ (‘magit-push-current’) + This command pushes the current branch to a branch read in the + minibuffer. + +‘P o’ (‘magit-push-other’) + This command pushes an arbitrary branch or commit somewhere. Both + the source and the target are read in the minibuffer. + +‘P r’ (‘magit-push-refspecs’) + This command pushes one or multiple refspecs to a remote, both of + which are read in the minibuffer. + + To use multiple refspecs, separate them with commas. Completion is + only available for the part before the colon, or when no colon is + used. + +‘P m’ (‘magit-push-matching’) + This command pushes all matching branches to another repository. + + If only one remote exists, then push to that. Otherwise prompt for + a remote, offering the remote configured for the current branch as + default. + +‘P t’ (‘magit-push-tags’) + This command pushes all tags to another repository. + + If only one remote exists, then push to that. Otherwise prompt for + a remote, offering the remote configured for the current branch as + default. + +‘P T’ (‘magit-push-tag’) + This command pushes a tag to another repository. + + One of the infix arguments, ‘--force-with-lease’, deserves a word of +caution. It is passed without a value, which means "permit a force push +as long as the remote-tracking branches match their counterparts on the +remote end". If you’ve set up a tool to do automatic fetches (Magit +itself does not provide such functionality), using ‘--force-with-lease’ +can be dangerous because you don’t actually control or know the state of +the remote-tracking refs. In that case, you should consider setting +‘push.useForceIfIncludes’ to ‘true’ (available since Git 2.30). + + Two more push commands exist, which by default are not available from +the push transient. See their doc-strings for instructions on how to +add them to the transient. + + -- Command: magit-push-implicitly args + This command pushes somewhere without using an explicit refspec. + + This command simply runs ‘git push -v [ARGS]’. ARGS are the infix + arguments. No explicit refspec arguments are used. Instead the + behavior depends on at least these Git variables: ‘push.default’, + ‘remote.pushDefault’, ‘branch.<branch>.pushRemote’, + ‘branch.<branch>.remote’, ‘branch.<branch>.merge’, and + ‘remote.<remote>.push’. + + If you add this suffix to a transient prefix without explicitly + specifying the description, then an attempt is made to predict what + this command will do. For example: + + (transient-insert-suffix 'magit-push \"p\" + '(\"i\" magit-push-implicitly))" + + -- Command: magit-push-to-remote remote args + This command pushes to the remote REMOTE without using an explicit + refspec. The remote is read in the minibuffer. + + This command simply runs ‘git push -v [ARGS] REMOTE’. ARGS are the + infix arguments. No refspec arguments are used. Instead the + behavior depends on at least these Git variables: ‘push.default’, + ‘remote.pushDefault’, ‘branch.<branch>.pushRemote’, + ‘branch.<branch>.remote’, ‘branch.<branch>.merge’, and + ‘remote.<remote>.push’. + + +File: magit.info, Node: Plain Patches, Next: Maildir Patches, Prev: Pushing, Up: Transferring + +7.5 Plain Patches +================= + +‘W’ (‘magit-patch’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘W c’ (‘magit-patch-create’) + This command creates patches for a set commits. If the region + marks several commits, then it creates patches for all of them. + Otherwise it functions as a transient prefix command, which + features several infix arguments and binds itself as a suffix + command. When this command is invoked as a suffix of itself, then + it creates a patch using the specified infix arguments. + +‘w a’ (‘magit-patch-apply’) + This command applies a patch. This is a transient prefix command, + which features several infix arguments and binds itself as a suffix + command. When this command is invoked as a suffix of itself, then + it applies a patch using the specified infix arguments. + +‘W s’ (‘magit-patch-save’) + This command creates a patch from the current diff. + + Inside ‘magit-diff-mode’ or ‘magit-revision-mode’ buffers, ‘C-x + C-w’ is also bound to this command. + + It is also possible to save a plain patch file by using ‘C-x C-w’ +inside a ‘magit-diff-mode’ or ‘magit-revision-mode’ buffer. + + +File: magit.info, Node: Maildir Patches, Prev: Plain Patches, Up: Transferring + +7.6 Maildir Patches +=================== + +Also see *note (gitman)git-am::. and *note (gitman)git-apply::. + +‘w’ (‘magit-am’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘w w’ (‘magit-am-apply-patches’) + This command applies one or more patches. If the region marks + files, then those are applied as patches. Otherwise this command + reads a file-name in the minibuffer, defaulting to the file at + point. + +‘w m’ (‘magit-am-apply-maildir’) + This command applies patches from a maildir. + +‘w a’ (‘magit-patch-apply’) + This command applies a plain patch. For a longer description see + *note Plain Patches::. This command is only available from the + ‘magit-am’ transient for historic reasons. + + When an "am" operation is in progress, then the transient instead +features the following suffix commands. + +‘w w’ (‘magit-am-continue’) + This command resumes the current patch applying sequence. + +‘w s’ (‘magit-am-skip’) + This command skips the stopped at patch during a patch applying + sequence. + +‘w a’ (‘magit-am-abort’) + This command aborts the current patch applying sequence. This + discards all changes made since the sequence started. + + +File: magit.info, Node: Miscellaneous, Next: Customizing, Prev: Transferring, Up: Top + +8 Miscellaneous +*************** + +* Menu: + +* Tagging:: +* Notes:: +* Submodules:: +* Subtree:: +* Worktree:: +* Sparse checkouts:: +* Bundle:: +* Common Commands:: +* Wip Modes:: +* Commands for Buffers Visiting Files:: +* Minor Mode for Buffers Visiting Blobs:: + + +File: magit.info, Node: Tagging, Next: Notes, Up: Miscellaneous + +8.1 Tagging +=========== + +Also see *note (gitman)git-tag::. + +‘t’ (‘magit-tag’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘t t’ (‘magit-tag-create’) + This command creates a new tag with the given NAME at REV. With a + prefix argument it creates an annotated tag. + +‘t r’ (‘magit-tag-release’) + This commands creates a release tag. It assumes that release tags + match ‘magit-release-tag-regexp’. + + First it prompts for the name of the new tag using the highest + existing tag as initial input and leaving it to the user to + increment the desired part of the version string. If you use + unconventional release tags or version numbers (e.g., + ‘v1.2.3-custom.1’), you can set the ‘magit-release-tag-regexp’ and + ‘magit-tag-version-regexp-alist’ variables. + + If ‘--annotate’ is enabled then it prompts for the message of the + new tag. The proposed tag message is based on the message of the + highest tag, provided that that contains the corresponding version + string and substituting the new version string for that. Otherwise + it proposes something like "Foo-Bar 1.2.3", given, for example, a + TAG "v1.2.3" and a repository located at something like + "/path/to/foo-bar". + +‘t k’ (‘magit-tag-delete’) + This command deletes one or more tags. If the region marks + multiple tags (and nothing else), then it offers to delete those. + Otherwise, it prompts for a single tag to be deleted, defaulting to + the tag at point. + +‘t p’ (‘magit-tag-prune’) + This command offers to delete tags missing locally from REMOTE, and + vice versa. + + +File: magit.info, Node: Notes, Next: Submodules, Prev: Tagging, Up: Miscellaneous + +8.2 Notes +========= + +Also see *note (gitman)git-notes::. + +‘T’ (‘magit-notes’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + +‘T T’ (‘magit-notes-edit’) + Edit the note attached to a commit, defaulting to the commit at + point. + + By default use the value of Git variable ‘core.notesRef’ or + "refs/notes/commits" if that is undefined. + +‘T r’ (‘magit-notes-remove’) + Remove the note attached to a commit, defaulting to the commit at + point. + + By default use the value of Git variable ‘core.notesRef’ or + "refs/notes/commits" if that is undefined. + +‘T p’ (‘magit-notes-prune’) + Remove notes about unreachable commits. + + It is possible to merge one note ref into another. That may result +in conflicts which have to resolved in the temporary worktree +".git/NOTES_MERGE_WORKTREE". + +‘T m’ (‘magit-notes-merge’) + Merge the notes of a ref read from the user into the current notes + ref. The current notes ref is the value of Git variable + ‘core.notesRef’ or "refs/notes/commits" if that is undefined. + + When a notes merge is in progress then the transient features the +following suffix commands, instead of those listed above. + +‘T c’ (‘magit-notes-merge-commit’) + Commit the current notes ref merge, after manually resolving + conflicts. + +‘T a’ (‘magit-notes-merge-abort’) + Abort the current notes ref merge. + + The following variables control what notes reference ‘magit-notes-*’, +‘git notes’ and ‘git show’ act on and display. Both the local and +global values are displayed and can be modified. + + -- Variable: core.notesRef + This variable specifies the notes ref that is displayed by default + and which commands act on by default. + + -- Variable: notes.displayRef + This variable specifies additional notes ref to be displayed in + addition to the ref specified by ‘core.notesRef’. It can have + multiple values and may end with ‘*’ to display all refs in the + ‘refs/notes/’ namespace (or ‘**’ if some names contain slashes). + + +File: magit.info, Node: Submodules, Next: Subtree, Prev: Notes, Up: Miscellaneous + +8.3 Submodules +============== + +Also see *note (gitman)git-submodule::. + +* Menu: + +* Listing Submodules:: +* Submodule Transient:: + + +File: magit.info, Node: Listing Submodules, Next: Submodule Transient, Up: Submodules + +8.3.1 Listing Submodules +------------------------ + +The command ‘magit-list-submodules’ displays a list of the current +repository’s submodules in a separate buffer. It’s also possible to +display information about submodules directly in the status buffer of +the super-repository by adding ‘magit-insert-modules’ to the hook +‘magit-status-sections-hook’ as described in *note Status Module +Sections::. + + -- Command: magit-list-submodules + This command displays a list of the current repository’s populated + submodules in a separate buffer. + + It can be invoked by pressing ‘RET’ on the section titled + "Modules". + + -- User Option: magit-submodule-list-columns + This option controls what columns are displayed by the command + ‘magit-list-submodules’ and how they are displayed. + + Each element has the form ‘(HEADER WIDTH FORMAT PROPS)’. + + HEADER is the string displayed in the header. WIDTH is the width + of the column. FORMAT is a function that is called with one + argument, the repository identification (usually its basename), and + with ‘default-directory’ bound to the toplevel of its working tree. + It has to return a string to be inserted or nil. PROPS is an alist + that supports the keys ‘:right-align’, ‘:pad-right’ and ‘:sort’. + + The ‘:sort’ function has a weird interface described in the + docstring of ‘tabulated-list--get-sort’. Alternatively ‘<’ and + ‘magit-repolist-version<’ can be used as those functions are + automatically replaced with functions that satisfy the interface. + Set ‘:sort’ to ‘nil’ to inhibit sorting; if unspecified, then the + column is sortable using the default sorter. + + You may wish to display a range of numeric columns using just one + character per column and without any padding between columns, in + which case you should use an appropriate HEADER, set WIDTH to 1, + and set ‘:pad-right’ to 9. ‘+’ is substituted for numbers higher + than 9. + + +File: magit.info, Node: Submodule Transient, Prev: Listing Submodules, Up: Submodules + +8.3.2 Submodule Transient +------------------------- + +‘o’ (‘magit-submodule’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + Some of the below commands default to act on the modules that are +selected using the region. For brevity their description talk about +"the selected modules", but if no modules are selected, then they act on +the current module instead, or if point isn’t on a module, then the read +a single module to act on. With a prefix argument these commands ignore +the selection and the current module and instead act on all suitable +modules. + +‘o a’ (‘magit-submodule-add’) + This commands adds the repository at URL as a module. Optional + PATH is the path to the module relative to the root of the + super-project. If it is nil then the path is determined based on + URL. + +‘o r’ (‘magit-submodule-register’) + This command registers the selected modules by copying their urls + from ".gitmodules" to "$GIT_DIR/config". These values can then be + edited before running ‘magit-submodule-populate’. If you don’t + need to edit any urls, then use the latter directly. + +‘o p’ (‘magit-submodule-populate’) + This command creates the working directory or directories of the + selected modules, checking out the recorded commits. + +‘o u’ (‘magit-submodule-update’) + This command updates the selected modules checking out the recorded + commits. + +‘o s’ (‘magit-submodule-synchronize’) + This command synchronizes the urls of the selected modules, copying + the values from ".gitmodules" to the ".git/config" of the + super-project as well those of the modules. + +‘o d’ (‘magit-submodule-unpopulate’) + This command removes the working directory of the selected modules. + +‘o l’ (‘magit-list-submodules’) + This command displays a list of the current repository’s modules. + +‘o f’ (‘magit-fetch-modules’) + This command fetches all populated modules. With a prefix + argument, it acts as a transient prefix command, allowing the + caller to set options. + + Also fetch the super-repository, because ‘git fetch’ does not + support not doing that. + + +File: magit.info, Node: Subtree, Next: Worktree, Prev: Submodules, Up: Miscellaneous + +8.4 Subtree +=========== + +Also see *note (gitman)git-subtree::. + +‘O’ (‘magit-subtree’) + This transient prefix command binds the two sub-transients; one for + importing a subtree and one for exporting a subtree. + +‘O i’ (‘magit-subtree-import’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + The suffixes of this command import subtrees. + + If the ‘--prefix’ argument is set, then the suffix commands use + that prefix without prompting the user. If it is unset, then they + read the prefix in the minibuffer. + +‘O i a’ (‘magit-subtree-add’) + This command adds COMMIT from REPOSITORY as a new subtree at + PREFIX. + +‘O i c’ (‘magit-subtree-add-commit’) + This command add COMMIT as a new subtree at PREFIX. + +‘O i m’ (‘magit-subtree-merge’) + This command merges COMMIT into the PREFIX subtree. + +‘O i f’ (‘magit-subtree-pull’) + This command pulls COMMIT from REPOSITORY into the PREFIX subtree. + +‘O e’ (‘magit-subtree-export’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + The suffixes of this command export subtrees. + + If the ‘--prefix’ argument is set, then the suffix commands use + that prefix without prompting the user. If it is unset, then they + read the prefix in the minibuffer. + +‘O e p’ (‘magit-subtree-push’) + This command extract the history of the subtree PREFIX and pushes + it to REF on REPOSITORY. + +‘O e s’ (‘magit-subtree-split’) + This command extracts the history of the subtree PREFIX. + + +File: magit.info, Node: Worktree, Next: Sparse checkouts, Prev: Subtree, Up: Miscellaneous + +8.5 Worktree +============ + +Also see *note (gitman)git-worktree::. + +‘Z’ (‘magit-worktree’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + +‘Z b’ (‘magit-worktree-checkout’) + Checkout BRANCH in a new worktree at PATH. + +‘Z c’ (‘magit-worktree-branch’) + Create a new BRANCH and check it out in a new worktree at PATH. + +‘Z m’ (‘magit-worktree-move’) + Move an existing worktree to a new PATH. + +‘Z k’ (‘magit-worktree-delete’) + Delete a worktree, defaulting to the worktree at point. The + primary worktree cannot be deleted. + +‘Z g’ (‘magit-worktree-status’) + Show the status for the worktree at point. + + If there is no worktree at point, then read one in the minibuffer. + If the worktree at point is the one whose status is already being + displayed in the current buffer, then show it in Dired instead. + + +File: magit.info, Node: Sparse checkouts, Next: Bundle, Prev: Worktree, Up: Miscellaneous + +8.6 Sparse checkouts +==================== + +Sparse checkouts provide a way to restrict the working tree to a subset +of directories. See *note (gitman)git-sparse-checkout::. + + *Warning*: Git introduced the ‘git sparse-checkout’ command in +version 2.25 and still advertises it as experimental and subject to +change. Magit’s interface should be considered the same. In +particular, if Git introduces a backward incompatible change, Magit’s +sparse checkout functionality may be updated in a way that requires a +more recent Git version. + +‘>’ (‘magit-sparse-checkout’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + +‘> e’ (‘magit-sparse-checkout-enable’) + This command initializes a sparse checkout that includes only the + files in the top-level directory. + + Note that ‘magit-sparse-checkout-set’ and + ‘magit-sparse-checkout-add’ automatically initialize a sparse + checkout if necessary. However, you may want to call + ‘magit-sparse-checkout-enable’ explicitly to re-initialize a sparse + checkout after calling ‘magit-sparse-checkout-disable’, to pass + additional arguments to ‘git sparse-checkout init’, or to execute + the initialization asynchronously. + +‘> s’ (‘magit-sparse-checkout-set’) + This command takes a list of directories and configures the sparse + checkout to include only files in those subdirectories. Any + previously included directories are excluded unless they are in the + provided list of directories. + +‘> a’ (‘magit-sparse-checkout-add’) + This command is like ‘magit-sparse-checkout-set’, but instead adds + the specified list of directories to the set of directories that is + already included in the sparse checkout. + +‘> r’ (‘magit-sparse-checkout-reapply’) + This command applies the currently configured sparse checkout + patterns to the working tree. This is useful to call if excluded + files have been checked out after operations such as merging or + rebasing. + +‘> d’ (‘magit-sparse-checkout-disable’) + This command restores the full checkout. To return to the previous + sparse checkout, call ‘magit-sparse-checkout-enable’. + + A sparse checkout can also be initiated when cloning a repository by +using the ‘magit-clone-sparse’ command in the ‘magit-clone’ transient +(see *note Cloning Repository::). + + If you want the status buffer to indicate when a sparse checkout is +enabled, add the function ‘magit-sparse-checkout-insert-header’ to +‘magit-status-headers-hook’. + + +File: magit.info, Node: Bundle, Next: Common Commands, Prev: Sparse checkouts, Up: Miscellaneous + +8.7 Bundle +========== + +Also see *note (gitman)git-bundle::. + + -- Command: magit-bundle + This transient prefix command binds several suffix commands for + running ‘git bundle’ subcommands and displays them in a temporary + buffer until a suffix is invoked. + + +File: magit.info, Node: Common Commands, Next: Wip Modes, Prev: Bundle, Up: Miscellaneous + +8.8 Common Commands +=================== + + -- Command: magit-switch-to-repository-buffer + -- Command: magit-switch-to-repository-buffer-other-window + -- Command: magit-switch-to-repository-buffer-other-frame + -- Command: magit-display-repository-buffer + These commands read any existing Magit buffer that belongs to the + current repository from the user and then switch to the selected + buffer (without refreshing it). + + The last variant uses ‘magit-display-buffer’ to do so and thus + respects ‘magit-display-buffer-function’. + + These are some of the commands that can be used in all buffers whose +major-modes derive from ‘magit-mode’. There are other common commands +beside the ones below, but these didn’t fit well anywhere else. + +‘C-w’ (‘magit-copy-section-value’) + This command saves the value of the current section to the + ‘kill-ring’, and, provided that the current section is a commit, + branch, or tag section, it also pushes the (referenced) revision to + the ‘magit-revision-stack’. + + When the current section is a branch or a tag, and a prefix + argument is used, then it saves the revision at its tip to the + ‘kill-ring’ instead of the reference name. + + When the region is active, this command saves that to the + ‘kill-ring’, like ‘kill-ring-save’ would, instead of behaving as + described above. If a prefix argument is used and the region is + within a hunk, then it strips the diff marker column and keeps only + either the added or removed lines, depending on the sign of the + prefix argument. + +‘M-w’ (‘magit-copy-buffer-revision’) + This command saves the revision being displayed in the current + buffer to the ‘kill-ring’ and also pushes it to the + ‘magit-revision-stack’. It is mainly intended for use in + ‘magit-revision-mode’ buffers, the only buffers where it is always + unambiguous exactly which revision should be saved. + + Most other Magit buffers usually show more than one revision, in + some way or another, so this command has to select one of them, and + that choice might not always be the one you think would have been + the best pick. + + Outside of Magit ‘M-w’ and ‘C-w’ are usually bound to +‘kill-ring-save’ and ‘kill-region’, and these commands would also be +useful in Magit buffers. Therefore when the region is active, then both +of these commands behave like ‘kill-ring-save’ instead of as described +above. + + +File: magit.info, Node: Wip Modes, Next: Commands for Buffers Visiting Files, Prev: Common Commands, Up: Miscellaneous + +8.9 Wip Modes +============= + +Git keeps *committed* changes around long enough for users to recover +changes they have accidentally deleted. It does so by not garbage +collecting any committed but no longer referenced objects for a certain +period of time, by default 30 days. + + But Git does *not* keep track of *uncommitted* changes in the working +tree and not even the index (the staging area). Because Magit makes it +so convenient to modify uncommitted changes, it also makes it easy to +shoot yourself in the foot in the process. + + For that reason Magit provides a global mode that saves *tracked* +files to work-in-progress references after or before certain actions. +(At present untracked files are never saved and for technical reasons +nothing is saved before the first commit has been created). + + Two separate work-in-progress references are used to track the state +of the index and of the working tree: ‘refs/wip/index/<branchref>’ and +‘refs/wip/wtree/<branchref>’, where ‘<branchref>’ is the full ref of the +current branch, e.g., ‘refs/heads/master’. When the ‘HEAD’ is detached +then ‘HEAD’ is used in place of ‘<branchref>’. + + Checking out another branch (or detaching ‘HEAD’) causes the use of +different wip refs for subsequent changes. + + -- User Option: magit-wip-mode + When this mode is enabled, then uncommitted changes are committed + to dedicated work-in-progress refs whenever appropriate (i.e., when + dataloss would be a possibility otherwise). + + Setting this variable directly does not take effect; either use the + Custom interface to do so or call the respective mode function. + + For historic reasons this mode is implemented on top of four other + ‘magit-wip-*’ modes, which can also be used individually, if you + want finer control over when the wip refs are updated; but that is + discouraged. See *note Legacy Wip Modes::. + + To view the log for a branch and its wip refs use the commands +‘magit-wip-log’ and ‘magit-wip-log-current’. You should use ‘--graph’ +when using these commands. + + -- Command: magit-wip-log + This command shows the log for a branch and its wip refs. With a + negative prefix argument only the worktree wip ref is shown. + + The absolute numeric value of the prefix argument controls how many + "branches" of each wip ref are shown. This is only relevant if the + value of ‘magit-wip-merge-branch’ is ‘nil’. + + -- Command: magit-wip-log-current + This command shows the log for the current branch and its wip refs. + With a negative prefix argument only the worktree wip ref is shown. + + The absolute numeric value of the prefix argument controls how many + "branches" of each wip ref are shown. This is only relevant if the + value of ‘magit-wip-merge-branch’ is ‘nil’. + +‘X w’ (‘magit-reset-worktree’) + This command resets the working tree to some commit read from the + user and defaulting to the commit at point, while keeping the + ‘HEAD’ and index as-is. + + This can be used to restore files to the state committed to a wip + ref. Note that this will discard any unstaged changes that might + have existed before invoking this command (but of course only after + committing that to the working tree wip ref). + + Note that even if you enable ‘magit-wip-mode’ this won’t give you +perfect protection. The most likely scenario for losing changes despite +the use of ‘magit-wip-mode’ is making a change outside Emacs and then +destroying it also outside Emacs. In some such a scenario, Magit, being +an Emacs package, didn’t get the opportunity to keep you from shooting +yourself in the foot. + + When you are unsure whether Magit did commit a change to the wip +refs, then you can explicitly request that all changes to all tracked +files are being committed. + +‘M-x magit-wip-commit’ + This command commits all changes to all tracked files to the index + and working tree work-in-progress refs. Like the modes described + above, it does not commit untracked files, but it does check all + tracked files for changes. Use this command when you suspect that + the modes might have overlooked a change made outside Emacs/Magit. + + -- User Option: magit-wip-namespace + The namespace used for work-in-progress refs. It has to end with a + slash. The wip refs are named ‘<namespace>index/<branchref>’ and + ‘<namespace>wtree/<branchref>’. When snapshots are created while + the ‘HEAD’ is detached then ‘HEAD’ is used in place of + ‘<branchref>’. + + -- User Option: magit-wip-mode-lighter + Mode-line lighter for ‘magit-wip--mode’. + +* Menu: + +* Wip Graph:: +* Legacy Wip Modes:: + + +File: magit.info, Node: Wip Graph, Next: Legacy Wip Modes, Up: Wip Modes + +8.9.1 Wip Graph +--------------- + + -- User Option: magit-wip-merge-branch + This option controls whether the current branch is merged into the + wip refs after a new commit was created on the branch. + + If non-nil and the current branch has new commits, then it is + merged into the wip ref before creating a new wip commit. This + makes it easier to inspect wip history and the wip commits are + never garbage collected. + + If nil and the current branch has new commits, then the wip ref is + reset to the tip of the branch before creating a new wip commit. + With this setting wip commits are eventually garbage collected. + + When ‘magit-wip-merge-branch’ is ‘t’, then the history looks like +this: + + *--*--*--*--*--* refs/wip/index/refs/heads/master + / / / + A-----B-----C refs/heads/master + + When ‘magit-wip-merge-branch’ is ‘nil’, then creating a commit on the +real branch and then making a change causes the wip refs to be recreated +to fork from the new commit. But the old commits on the wip refs are +not lost. They are still available from the reflog. To make it easier +to see when the fork point of a wip ref was changed, an additional +commit with the message "restart autosaving" is created on it (‘xxO’ +commits below are such boundary commits). + + Starting with + + BI0---BI1 refs/wip/index/refs/heads/master + / + A---B refs/heads/master + \ + BW0---BW1 refs/wip/wtree/refs/heads/master + + and committing the staged changes and editing and saving a file would +result in + + BI0---BI1 refs/wip/index/refs/heads/master + / + A---B---C refs/heads/master + \ \ + \ CW0---CW1 refs/wip/wtree/refs/heads/master + \ + BW0---BW1 refs/wip/wtree/refs/heads/master@{2} + + The fork-point of the index wip ref is not changed until some change +is being staged. Likewise just checking out a branch or creating a +commit does not change the fork-point of the working tree wip ref. The +fork-points are not adjusted until there actually is a change that +should be committed to the respective wip ref. + + +File: magit.info, Node: Legacy Wip Modes, Prev: Wip Graph, Up: Wip Modes + +8.9.2 Legacy Wip Modes +---------------------- + +It is recommended that you use the mode ‘magit-wip-mode’ (which see) and +ignore the existence of the following modes, which are preserved for +historic reasons. + + Setting the following variables directly does not take effect; either +use the Custom interface to do so or call the respective mode functions. + + -- User Option: magit-wip-after-save-mode + When this mode is enabled, then saving a buffer that visits a file + tracked in a Git repository causes its current state to be + committed to the working tree wip ref for the current branch. + + -- User Option: magit-wip-after-apply-mode + When this mode is enabled, then applying (i.e., staging, unstaging, + discarding, reversing, and regularly applying) a change to a file + tracked in a Git repository causes its current state to be + committed to the index and/or working tree wip refs for the current + branch. + + If you only ever edit files using Emacs and only ever interact with +Git using Magit, then the above two modes should be enough to protect +each and every change from accidental loss. In practice nobody does +that. Two additional modes exists that do commit to the wip refs before +making changes that could cause the loss of earlier changes. + + -- User Option: magit-wip-before-change-mode + When this mode is enabled, then certain commands commit the + existing changes to the files they are about to make changes to. + + -- User Option: magit-wip-initial-backup-mode + When this mode is enabled, then the current version of a file is + committed to the worktree wip ref before the buffer visiting that + file is saved for the first time since the buffer was created. + + This backs up the same version of the file that ‘backup-buffer’ + would save. While ‘backup-buffer’ uses a backup file, this mode + uses the same worktree wip ref as used by the other Magit Wip + modes. Like ‘backup-buffer’, it only does this once; unless you + kill the buffer and visit the file again only one backup will be + created per Emacs session. + + This mode ignores the variables that affect ‘backup-buffer’ and can + be used along-side that function, which is recommended because it + only backs up files that are tracked in a Git repository. + + -- User Option: magit-wip-after-save-local-mode-lighter + Mode-line lighter for ‘magit-wip-after-save-local-mode’. + + -- User Option: magit-wip-after-apply-mode-lighter + Mode-line lighter for ‘magit-wip-after-apply-mode’. + + -- User Option: magit-wip-before-change-mode-lighter + Mode-line lighter for ‘magit-wip-before-change-mode’. + + -- User Option: magit-wip-initial-backup-mode-lighter + Mode-line lighter for ‘magit-wip-initial-backup-mode’. + + +File: magit.info, Node: Commands for Buffers Visiting Files, Next: Minor Mode for Buffers Visiting Blobs, Prev: Wip Modes, Up: Miscellaneous + +8.10 Commands for Buffers Visiting Files +======================================== + +By default Magit defines a few global key bindings. These bindings are +a compromise between providing no bindings at all and providing the +better bindings I would have liked to use instead. Magit cannot provide +the set of recommended bindings by default because those key sequences +are strictly reserved for bindings added by the user. Also see *note +Global Bindings:: and *note (elisp)Key Binding Conventions::. + + To use the recommended bindings, add this to your init file and +restart Emacs. + + (setq magit-define-global-key-bindings 'recommended) + + If you don’t want Magit to add any bindings to the global keymap at +all, add this to your init file and restart Emacs. + + (setq magit-define-global-key-bindings nil) + +‘C-c f’ (‘magit-file-dispatch’) +‘C-c f s’ (‘magit-stage-file’) +‘C-c f s’ (‘magit-stage-buffer-file’) +‘C-c f u’ (‘magit-unstage-file’) +‘C-c f u’ (‘magit-unstage-buffer-file’) +‘C-c f , x’ (‘magit-file-untrack’) +‘C-c f , r’ (‘magit-file-rename’) +‘C-c f , k’ (‘magit-file-delete’) +‘C-c f , c’ (‘magit-file-checkout’) +‘C-c f D’ (‘magit-diff’) +‘C-c f d’ (‘magit-diff-buffer-file’) +‘C-c f L’ (‘magit-log’) +‘C-c f l’ (‘magit-log-buffer-file’) +‘C-c f t’ (‘magit-log-trace-definition’) +‘C-c f M’ (‘magit-log-merged’) +‘C-c f B’ (‘magit-blame’) +‘C-c f b’ (‘magit-blame-additions’) +‘C-c f r’ (‘magit-blame-removal’) +‘C-c f f’ (‘magit-blame-reverse’) +‘C-c f m’ (‘magit-blame-echo’) +‘C-c f q’ (‘magit-blame-quit’) +‘C-c f p’ (‘magit-blob-previous’) +‘C-c f n’ (‘magit-blob-next’) +‘C-c f v’ (‘magit-find-file’) +‘C-c f V’ (‘magit-blob-visit-file’) +‘C-c f g’ (‘magit-status-here’) +‘C-c f G’ (‘magit-display-repository-buffer’) +‘C-c f c’ (‘magit-commit’) +‘C-c f e’ (‘magit-edit-line-commit’) + Each of these commands is documented individually right below, + alongside their default key bindings. The bindings shown above are + the recommended bindings, which you can enable by following the + instructions further up. + +‘C-c M-g’ (‘magit-file-dispatch’) + This transient prefix command binds the following suffix commands + and displays them in a temporary buffer until a suffix is invoked. + +‘C-c M-g s’ (‘magit-stage-file’) +‘C-c M-g s’ (‘magit-stage-buffer-file’) + Stage all changes to the file being visited in the current buffer. + When not visiting a file, then the first command is used, which + prompts for a file. + +‘C-c M-g u’ (‘magit-unstage-file’) +‘C-c M-g u’ (‘magit-unstage-buffer-file’) + Unstage all changes to the file being visited in the current + buffer. When not visiting a file, then the first command is used, + which prompts for a file. + +‘C-c M-g , x’ (‘magit-file-untrack’) + This command untracks a file read from the user, defaulting to the + visited file. + +‘C-c M-g , r’ (‘magit-file-rename’) + This command renames a file read from the user, defaulting to the + visited file. + +‘C-c M-g , k’ (‘magit-file-delete’) + This command deletes a file read from the user, defaulting to the + visited file. + +‘C-c M-g , c’ (‘magit-file-checkout’) + This command updates a file in the working tree and index to the + contents from a revision. Both the revision and file are read from + the user. + +‘C-c M-g D’ (‘magit-diff’) + This transient prefix command binds several diff suffix commands + and infix arguments and displays them in a temporary buffer until a + suffix is invoked. See *note Diffing::. + + This is the same command that ‘d’ is bound to in Magit buffers. If + this command is invoked from a file-visiting buffer, then the + initial value of the option (‘--’) that limits the diff to certain + file(s) is set to the visited file. + +‘C-c M-g d’ (‘magit-diff-buffer-file’) + This command shows the diff for the file of blob that the current + buffer visits. + + -- User Option: magit-diff-buffer-file-locked + This option controls whether ‘magit-diff-buffer-file’ uses a + dedicated buffer. See *note Modes and Buffers::. + +‘C-c M-g L’ (‘magit-log’) + This transient prefix command binds several log suffix commands and + infix arguments and displays them in a temporary buffer until a + suffix is invoked. See *note Logging::. + + This is the same command that ‘l’ is bound to in Magit buffers. If + this command is invoked from a file-visiting buffer, then the + initial value of the option (‘--’) that limits the log to certain + file(s) is set to the visited file. + +‘C-c M-g l’ (‘magit-log-buffer-file’) + This command shows the log for the file of blob that the current + buffer visits. Renames are followed when a prefix argument is used + or when ‘--follow’ is an active log argument. When the region is + active, the log is restricted to the selected line range. + + -- User Option: magit-log-buffer-file-locked + This option controls whether ‘magit-log-buffer-file’ uses a + dedicated buffer. See *note Modes and Buffers::. + +‘C-c M-g t’ (‘magit-log-trace-definition’) + This command shows the log for the definition at point. + +‘C-c M-g M’ (‘magit-log-merged’) + This command reads a commit and a branch in shows a log concerning + the merge of the former into the latter. This shows multiple + commits even in case of a fast-forward merge. + +‘C-c M-g B’ (‘magit-blame’) + This transient prefix command binds all blaming suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. + + For more information about this and the following commands also see + *note Blaming::. + + In addition to the ‘magit-blame’ sub-transient, the dispatch + transient also binds several blaming suffix commands directly. See + *note Blaming:: for information about those commands and bindings. + +‘C-c M-g p’ (‘magit-blob-previous’) + This command visits the previous blob which modified the current + file. + +‘C-c M-g n’ (‘magit-blob-next’) + This command visits the next blob which modified the current file. + +‘C-c M-g v’ (‘magit-find-file’) + This command reads a revision and file and visits the respective + blob. + +‘C-c M-g V’ (‘magit-blob-visit-file’) + This command visits the file from the working tree, corresponding + to the current blob. When visiting a blob or the version from the + index, then it goes to the same location in the respective file in + the working tree. + +‘C-c M-g g’ (‘magit-status-here’) + This command displays the status of the current repository in a + buffer, like ‘magit-status’ does. Additionally it tries to go to + the position in that buffer, which corresponds to the position in + the current file-visiting buffer (if any). + +‘C-c M-g G’ (‘magit-display-repository-buffer’) + This command reads and displays a Magit buffer belonging to the + current repository, without refreshing it. + +‘C-c M-g c’ (‘magit-commit’) + This transient prefix command binds the following suffix commands + along with the appropriate infix arguments and displays them in a + temporary buffer until a suffix is invoked. See *note Initiating a + Commit::. + +‘C-c M-g e’ (‘magit-edit-line-commit’) + This command makes the commit editable that added the current line. + + With a prefix argument it makes the commit editable that removes + the line, if any. The commit is determined using ‘git blame’ and + made editable using ‘git rebase --interactive’ if it is reachable + from ‘HEAD’, or by checking out the commit (or a branch that points + at it) otherwise. + + +File: magit.info, Node: Minor Mode for Buffers Visiting Blobs, Prev: Commands for Buffers Visiting Files, Up: Miscellaneous + +8.11 Minor Mode for Buffers Visiting Blobs +========================================== + +The ‘magit-blob-mode’ enables certain Magit features in blob-visiting +buffers. Such buffers can be created using ‘magit-find-file’ and some +of the commands mentioned below, which also take care of turning on this +minor mode. Currently this mode only establishes a few key bindings, +but this might be extended. + +‘p’ (‘magit-blob-previous’) + Visit the previous blob which modified the current file. + +‘n’ (‘magit-blob-next’) + Visit the next blob which modified the current file. + +‘q’ (‘magit-kill-this-buffer’) + Kill the current buffer. + + +File: magit.info, Node: Customizing, Next: Plumbing, Prev: Miscellaneous, Up: Top + +9 Customizing +************* + +Both Git and Emacs are highly customizable. Magit is both a Git +porcelain as well as an Emacs package, so it makes sense to customize it +using both Git variables as well as Emacs options. However this +flexibility doesn’t come without problems, including but not limited to +the following. + + • Some Git variables automatically have an effect in Magit without + requiring any explicit support. Sometimes that is desirable - in + other cases, it breaks Magit. + + When a certain Git setting breaks Magit but you want to keep using + that setting on the command line, then that can be accomplished by + overriding the value for Magit only by appending something like + ‘("-c" "some.variable=compatible-value")’ to + ‘magit-git-global-arguments’. + + • Certain settings like ‘fetch.prune=true’ are respected by Magit + commands (because they simply call the respective Git command) but + their value is not reflected in the respective transient buffers. + In this case the ‘--prune’ argument in ‘magit-fetch’ might be + active or inactive, but that doesn’t keep the Git variable from + being honored by the suffix commands anyway. So pruning might + happen despite the ‘--prune’ arguments being displayed in a way + that seems to indicate that no pruning will happen. + + I intend to address these and similar issues in a future release. + +* Menu: + +* Per-Repository Configuration:: +* Essential Settings:: + + +File: magit.info, Node: Per-Repository Configuration, Next: Essential Settings, Up: Customizing + +9.1 Per-Repository Configuration +================================ + +Magit can be configured on a per-repository level using both Git +variables as well as Emacs options. + + To set a Git variable for one repository only, simply set it in +‘/path/to/repo/.git/config’ instead of ‘$HOME/.gitconfig’ or +‘/etc/gitconfig’. See *note (gitman)git-config::. + + Similarly, Emacs options can be set for one repository only by +editing ‘/path/to/repo/.dir-locals.el’. See *note (emacs)Directory +Variables::. For example to disable automatic refreshes of +file-visiting buffers in just one huge repository use this: + + • ‘/path/to/huge/repo/.dir-locals.el’ + + ((nil . ((magit-refresh-buffers . nil)))) + + It might only be costly to insert certain information into Magit +buffers for repositories that are exceptionally large, in which case you +can disable the respective section inserters just for that repository: + + • ‘/path/to/tag/invested/repo/.dir-locals.el’ + + ((magit-status-mode + . ((eval . (magit-disable-section-inserter 'magit-insert-tags-header))))) + + -- Function: magit-disable-section-inserter fn + This function disables the section inserter FN in the current + repository. It is only intended for use in ‘.dir-locals.el’ and + ‘.dir-locals-2.el’. + + If you want to apply the same settings to several, but not all, +repositories then keeping the repository-local config files in sync +would quickly become annoying. To avoid that you can create config +files for certain classes of repositories (e.g., "huge repositories") +and then include those files in the per-repository config files. For +example: + + • ‘/path/to/huge/repo/.git/config’ + + [include] + path = /path/to/huge-gitconfig + + • ‘/path/to/huge-gitconfig’ + + [status] + showUntrackedFiles = no + + • ‘$HOME/.emacs.d/init.el’ + + (dir-locals-set-class-variables 'huge-git-repository + '((nil . ((magit-refresh-buffers . nil))))) + + (dir-locals-set-directory-class + "/path/to/huge/repo/" 'huge-git-repository) + + +File: magit.info, Node: Essential Settings, Prev: Per-Repository Configuration, Up: Customizing + +9.2 Essential Settings +====================== + +The next three sections list and discuss several variables that many +users might want to customize, for safety and/or performance reasons. + +* Menu: + +* Safety:: +* Performance:: +* Global Bindings:: + + +File: magit.info, Node: Safety, Next: Performance, Up: Essential Settings + +9.2.1 Safety +------------ + +This section discusses various variables that you might want to change +(or *not* change) for safety reasons. + + Git keeps *committed* changes around long enough for users to recover +changes they have accidentally been deleted. It does not do the same +for *uncommitted* changes in the working tree and not even the index +(the staging area). Because Magit makes it so easy to modify +uncommitted changes, it also makes it easy to shoot yourself in the foot +in the process. For that reason Magit provides three global modes that +save *tracked* files to work-in-progress references after or before +certain actions. See *note Wip Modes::. + + These modes are not enabled by default because of performance +concerns. Instead a lot of potentially destructive commands require +confirmation every time they are used. In many cases this can be +disabled by adding a symbol to ‘magit-no-confirm’ (see *note Completion +and Confirmation::). If you enable the various wip modes then you +should add ‘safe-with-wip’ to this list. + + Similarly it isn’t necessary to require confirmation before moving a +file to the system trash - if you trashed a file by mistake then you can +recover it from there. Option ‘magit-delete-by-moving-to-trash’ +controls whether the system trash is used, which is the case by default. +Nevertheless, ‘trash’ isn’t a member of ‘magit-no-confirm’ - you might +want to change that. + + By default buffers visiting files are automatically reverted when the +visited file changes on disk. This isn’t as risky as it might seem, but +to make an informed decision you should see *note Risk of Reverting +Automatically::. + + +File: magit.info, Node: Performance, Next: Global Bindings, Prev: Safety, Up: Essential Settings + +9.2.2 Performance +----------------- + +After Magit has run ‘git’ for side-effects, it also refreshes the +current Magit buffer and the respective status buffer. This is +necessary because otherwise outdated information might be displayed +without the user noticing. Magit buffers are updated by recreating +their content from scratch, which makes updating simpler and less +error-prone, but also more costly. Keeping it simple and just +re-creating everything from scratch is an old design decision and +departing from that will require major refactoring. + + Meanwhile you can tell Magit to only automatically refresh the +current Magit buffer, but not the status buffer. If you do that, then +the status buffer is only refreshed automatically if it is the current +buffer. + + (setq magit-refresh-status-buffer nil) + + You should also check whether any third-party packages have added +anything to ‘magit-refresh-buffer-hook’, ‘magit-pre-refresh-hook’, and +‘magit-post-refresh-hook’. If so, then check whether those additions +impact performance significantly. + + Magit can be told to refresh buffers verbosely using ‘M-x +magit-toggle-verbose-refresh’. Enabling this helps figuring out which +sections are bottlenecks. Each line printed to the ‘*Messages*’ buffer +contains a section name, the number of seconds it took to show this +section, and from 0 to 2 exclamation marks: the more exclamation marks +the slower the section is. + + Magit also reverts buffers for visited files located inside the +current repository when the visited file changes on disk. That is +implemented on top of ‘auto-revert-mode’ from the built-in library +‘autorevert’. To figure out whether that impacts performance, check +whether performance is significantly worse, when many buffers exist +and/or when some buffers visit files using TRAMP. If so, then this +should help. + + (setq auto-revert-buffer-list-filter + 'magit-auto-revert-repository-buffer-p) + + For alternative approaches see *note Automatic Reverting of +File-Visiting Buffers::. + + If you have enabled any features that are disabled by default, then +you should check whether they impact performance significantly. It’s +likely that they were not enabled by default because it is known that +they reduce performance at least in large repositories. + + If performance is only slow inside certain unusually large +repositories, then you might want to disable certain features on a +per-repository or per-repository-class basis only. See *note +Per-Repository Configuration::. For example it takes a long time to +determine the next and current tag in repository with exceptional +numbers of tags. It would therefore be a good idea to disable +‘magit-insert-tags-headers’, as explained at the mentioned node. + +* Menu: + +* Microsoft Windows Performance:: +* MacOS Performance:: + +Log Performance +............... + +When showing logs, Magit limits the number of commits initially shown in +the hope that this avoids unnecessary work. When ‘--graph’ is used, +then this unfortunately does not have the desired effect for large +histories. Junio, Git’s maintainer, said on the Git mailing list +(<https://www.spinics.net/lists/git/msg232230.html>): "‘--graph’ wants +to compute the whole history and the max-count only affects the output +phase after ‘--graph’ does its computation". + + In other words, it’s not that Git is slow at outputting the +differences, or that Magit is slow at parsing the output - the problem +is that Git first goes outside and has a smoke. + + We actually work around this issue by limiting the number of commits +not only by using ‘-<N>’ but by also using a range. But unfortunately +that’s not always possible. + + When more than a few thousand commits are shown, then the use of +‘--graph’ can slow things down. + + Using ‘--color --graph’ is even slower. Magit uses code that is part +of Emacs to turn control characters into faces. That code is pretty +slow and this is quite noticeable when showing a log with many branches +and merges. For that reason ‘--color’ is not enabled by default +anymore. Consider leaving it at that. + +Diff Performance +................ + +If diffs are slow, then consider turning off some optional diff features +by setting all or some of the following variables to ‘nil’: +‘magit-diff-highlight-indentation’, ‘magit-diff-highlight-trailing’, +‘magit-diff-paint-whitespace’, ‘magit-diff-highlight-hunk-body’, and +‘magit-diff-refine-hunk’. + + When showing a commit instead of some arbitrary diff, then some +additional information is displayed. Calculating this information can +be quite expensive given certain circumstances. If looking at a commit +using ‘magit-revision-mode’ takes considerably more time than looking at +the same commit in ‘magit-diff-mode’, then consider setting +‘magit-revision-insert-related-refs’ to ‘nil’. + + When you are often confronted with diffs that contain deleted files, +then you might want to enable the ‘--irreversible-delete’ argument. If +you do that then diffs still show that a file was deleted but without +also showing the complete deleted content of the file. This argument is +not available by default, see *note (transient)Enabling and Disabling +Suffixes::. Once you have done that you should enable it and save that +setting, see *note (transient)Saving Values::. You should do this in +both the diff (‘d’) and the diff refresh (‘D’) transient popups. + +Refs Buffer Performance +....................... + +When refreshing the "references buffer" is slow, then that’s usually +because several hundred refs are being displayed. The best way to +address that is to display fewer refs, obviously. + + If you are not, or only mildly, interested in seeing the list of +tags, then start by not displaying them: + + (remove-hook 'magit-refs-sections-hook 'magit-insert-tags) + + Then you should also make sure that the listed remote branches +actually all exist. You can do so by pruning branches which no longer +exist using ‘f-pa’. + +Committing Performance +...................... + +When you initiate a commit, then Magit by default automatically shows a +diff of the changes you are about to commit. For large commits this can +take a long time, which is especially distracting when you are +committing large amounts of generated data which you don’t actually +intend to inspect before committing. This behavior can be turned off +using: + + (remove-hook 'server-switch-hook 'magit-commit-diff) + (remove-hook 'with-editor-filter-visit-hook 'magit-commit-diff) + + Then you can type ‘C-c C-d’ to show the diff when you actually want +to see it, but only then. Alternatively you can leave the hook alone +and just type ‘C-g’ in those cases when it takes too long to generate +the diff. If you do that, then you will end up with a broken diff +buffer, but doing it this way has the advantage that you usually get to +see the diff, which is useful because it increases the odds that you +spot potential issues. + + +File: magit.info, Node: Microsoft Windows Performance, Next: MacOS Performance, Up: Performance + +Microsoft Windows Performance +............................. + +In order to update the status buffer, ‘git’ has to be run a few dozen +times. That is problematic on Microsoft Windows, because that operating +system is exceptionally slow at starting processes. Sadly this is an +issue that can only be fixed by Microsoft itself, and they don’t appear +to be particularly interested in doing so. + + Beside the subprocess issue, there are also other Windows-specific +performance issues. Some of these have workarounds. The maintainers of +"Git for Windows" try to improve performance on Windows. Always use the +latest release in order to benefit from the latest performance tweaks. +Magit too tries to work around some Windows-specific issues. + + According to some sources, setting the following Git variables can +also help. + + git config --global core.preloadindex true # default since v2.1 + git config --global core.fscache true # default since v2.8 + git config --global gc.auto 256 + + You should also check whether an anti-virus program is affecting +performance. + + +File: magit.info, Node: MacOS Performance, Prev: Microsoft Windows Performance, Up: Performance + +MacOS Performance +................. + +Before Emacs 26.1 child processes were created using ‘fork’ on macOS. +That needlessly copied GUI resources, which is expensive. The result +was that forking took about 30 times as long on Darwin than on Linux, +and because Magit starts many ‘git’ processes that made quite a +difference. + + So make sure that you are using at least Emacs 26.1, in which case +the faster ‘vfork’ will be used. (The creation of child processes still +takes about twice as long on Darwin compared to Linux.) See (1) for +more information. + + Additionally, ‘git’ installed from a package manager like ‘brew’ or +‘nix’ seems to be slower than the native executable. Profile the ‘git’ +executable you’re running against the one at ‘/usr/bin/git’, and if you +notice a notable difference try using the latter as +‘magit-git-executable’. + + ---------- Footnotes ---------- + + (1) +<https://lists.gnu.org/archive/html/bug-gnu-emacs/2017-04/msg00201.html> + + +File: magit.info, Node: Global Bindings, Prev: Performance, Up: Essential Settings + +9.2.3 Global Bindings +--------------------- + + -- User Option: magit-define-global-key-bindings + This option controls which set of Magit key bindings, if any, may + be added to the global keymap, even before Magit is first used in + the current Emacs session. + + • If the value is ‘nil’, no bindings are added. + + • If ‘default’, maybe add: + + ‘C-x g’ ‘magit-status’ + ‘C-x M-g’ ‘magit-dispatch’ + ‘C-c M-g’ ‘magit-file-dispatch’ + + • If ‘recommended’, maybe add: + + ‘C-x g’ ‘magit-status’ + ‘C-c g’ ‘magit-dispatch’ + ‘C-c f’ ‘magit-file-dispatch’ + + These bindings are strongly recommended, but we cannot use + them by default, because the ‘C-c <LETTER>’ namespace is + strictly reserved for bindings added by the user (see *note + (elisp)Key Binding Conventions::). + + The bindings in the chosen set may be added when ‘after-init-hook’ + is run. Each binding is added if, and only if, at that time no + other key is bound to the same command, and no other command is + bound to the same key. In other words we try to avoid adding + bindings that are unnecessary, as well as bindings that conflict + with other bindings. + + Adding these bindings is delayed until ‘after-init-hook’ is run to + allow users to set the variable anywhere in their init file + (without having to make sure to do so before ‘magit’ is loaded or + autoloaded) and to increase the likelihood that all the potentially + conflicting user bindings have already been added. + + To set this variable use either ‘setq’ or the Custom interface. Do + not use the function ‘customize-set-variable’ because doing that + would cause Magit to be loaded immediately, when that form is + evaluated (this differs from ‘custom-set-variables’, which doesn’t + load the libraries that define the customized variables). + + Setting this variable has no effect if ‘after-init-hook’ has + already been run. + + +File: magit.info, Node: Plumbing, Next: FAQ, Prev: Customizing, Up: Top + +10 Plumbing +*********** + +The following sections describe how to use several of Magit’s core +abstractions to extend Magit itself or implement a separate extension. + + A few of the low-level features used by Magit have been factored out +into separate libraries/packages, so that they can be used by other +packages, without having to depend on Magit. See *note +(with-editor)Top:: for information about ‘with-editor’. ‘transient’ +doesn’t have a manual yet. + + If you are trying to find an unused key that you can bind to a +command provided by your own Magit extension, then checkout +<https://github.com/magit/magit/wiki/Plugin-Dispatch-Key-Registry>. + +* Menu: + +* Calling Git:: +* Section Plumbing:: +* Refreshing Buffers:: +* Conventions:: + + +File: magit.info, Node: Calling Git, Next: Section Plumbing, Up: Plumbing + +10.1 Calling Git +================ + +Magit provides many specialized functions for calling Git. All of these +functions are defined in either ‘magit-git.el’ or ‘magit-process.el’ and +have one of the prefixes ‘magit-run-’, ‘magit-call-’, ‘magit-start-’, or +‘magit-git-’ (which is also used for other things). + + All of these functions accept an indefinite number of arguments, +which are strings that specify command line arguments for Git (or in +some cases an arbitrary executable). These arguments are flattened +before being passed on to the executable; so instead of strings they can +also be lists of strings and arguments that are ‘nil’ are silently +dropped. Some of these functions also require a single mandatory +argument before these command line arguments. + + Roughly speaking, these functions run Git either to get some value or +for side-effects. The functions that return a value are useful to +collect the information necessary to populate a Magit buffer, while the +others are used to implement Magit commands. + + The functions in the value-only group always run synchronously, and +they never trigger a refresh. The function in the side-effect group can +be further divided into subgroups depending on whether they run Git +synchronously or asynchronously, and depending on whether they trigger a +refresh when the executable has finished. + +* Menu: + +* Getting a Value from Git:: +* Calling Git for Effect:: + + +File: magit.info, Node: Getting a Value from Git, Next: Calling Git for Effect, Up: Calling Git + +10.1.1 Getting a Value from Git +------------------------------- + +These functions run Git in order to get a value, an exit status, or +output. Of course you could also use them to run Git commands that have +side-effects, but that should be avoided. + + -- Function: magit-git-exit-code &rest args + Executes git with ARGS and returns its exit code. + + -- Function: magit-git-success &rest args + Executes git with ARGS and returns ‘t’ if the exit code is ‘0’, + ‘nil’ otherwise. + + -- Function: magit-git-failure &rest args + Executes git with ARGS and returns ‘t’ if the exit code is ‘1’, + ‘nil’ otherwise. + + -- Function: magit-git-true &rest args + Executes git with ARGS and returns ‘t’ if the first line printed by + git is the string "true", ‘nil’ otherwise. + + -- Function: magit-git-false &rest args + Executes git with ARGS and returns ‘t’ if the first line printed by + git is the string "false", ‘nil’ otherwise. + + -- Function: magit-git-insert &rest args + Executes git with ARGS and inserts its output at point. + + -- Function: magit-git-string &rest args + Executes git with ARGS and returns the first line of its output. + If there is no output or if it begins with a newline character, + then this returns ‘nil’. + + -- Function: magit-git-lines &rest args + Executes git with ARGS and returns its output as a list of lines. + Empty lines anywhere in the output are omitted. + + -- Function: magit-git-items &rest args + Executes git with ARGS and returns its null-separated output as a + list. Empty items anywhere in the output are omitted. + + If the value of option ‘magit-git-debug’ is non-nil and git exits + with a non-zero exit status, then warn about that in the echo area + and add a section containing git’s standard error in the current + repository’s process buffer. + + -- Function: magit-process-git destination &rest args + Calls Git synchronously in a separate process, returning its exit + code. DESTINATION specifies how to handle the output, like for + ‘call-process’, except that file handlers are supported. Enables + Cygwin’s "noglob" option during the call and ensures unix eol + conversion. + + -- Function: magit-process-file process &optional infile buffer display + &rest args + Processes files synchronously in a separate process. Identical to + ‘process-file’ but temporarily enables Cygwin’s "noglob" option + during the call and ensures unix eol conversion. + + If an error occurs when using one of the above functions, then that +is usually due to a bug, i.e., using an argument which is not actually +supported. Such errors are usually not reported, but when they occur we +need to be able to debug them. + + -- User Option: magit-git-debug + Whether to report errors that occur when using ‘magit-git-insert’, + ‘magit-git-string’, ‘magit-git-lines’, or ‘magit-git-items’. This + does not actually raise an error. Instead a message is shown in + the echo area, and git’s standard error is insert into a new + section in the current repository’s process buffer. + + -- Function: magit-git-str &rest args + This is a variant of ‘magit-git-string’ that ignores the option + ‘magit-git-debug’. It is mainly intended to be used while handling + errors in functions that do respect that option. Using such a + function while handing an error could cause yet another error and + therefore lead to an infinite recursion. You probably won’t ever + need to use this function. + + +File: magit.info, Node: Calling Git for Effect, Prev: Getting a Value from Git, Up: Calling Git + +10.1.2 Calling Git for Effect +----------------------------- + +These functions are used to run git to produce some effect. Most Magit +commands that actually run git do so by using such a function. + + Because we do not need to consume git’s output when using these +functions, their output is instead logged into a per-repository buffer, +which can be shown using ‘$’ from a Magit buffer or ‘M-x magit-process’ +elsewhere. + + These functions can have an effect in two distinct ways. Firstly, +running git may change something, i.e., create or push a new commit. +Secondly, that change may require that Magit buffers are refreshed to +reflect the changed state of the repository. But refreshing isn’t +always desirable, so only some of these functions do perform such a +refresh after git has returned. + + Sometimes it is useful to run git asynchronously. For example, when +the user has just initiated a push, then there is no reason to make her +wait until that has completed. In other cases it makes sense to wait +for git to complete before letting the user do something else. For +example after staging a change it is useful to wait until after the +refresh because that also automatically moves to the next change. + + -- Function: magit-call-git &rest args + Calls git synchronously with ARGS. + + -- Function: magit-call-process program &rest args + Calls PROGRAM synchronously with ARGS. + + -- Function: magit-run-git &rest args + Calls git synchronously with ARGS and then refreshes. + + -- Function: magit-run-git-with-input &rest args + Calls git synchronously with ARGS and sends it the content of the + current buffer on standard input. + + If the current buffer’s ‘default-directory’ is on a remote + filesystem, this function actually runs git asynchronously. But + then it waits for the process to return, so the function itself is + synchronous. + + -- Function: magit-git &rest args + Calls git synchronously with ARGS for side-effects only. This + function does not refresh the buffer. + + -- Function: magit-git-wash washer &rest args + Execute Git with ARGS, inserting washed output at point. Actually + first insert the raw output at point. If there is no output call + ‘magit-cancel-section’. Otherwise temporarily narrow the buffer to + the inserted text, move to its beginning, and then call function + WASHER with ARGS as its sole argument. + + And now for the asynchronous variants. + + -- Function: magit-run-git-async &rest args + Start Git, prepare for refresh, and return the process object. + ARGS is flattened and then used as arguments to Git. + + Display the command line arguments in the echo area. + + After Git returns some buffers are refreshed: the buffer that was + current when this function was called (if it is a Magit buffer and + still alive), as well as the respective Magit status buffer. + Unmodified buffers visiting files that are tracked in the current + repository are reverted if ‘magit-revert-buffers’ is non-nil. + + -- Function: magit-run-git-with-editor &rest args + Export GIT_EDITOR and start Git. Also prepare for refresh and + return the process object. ARGS is flattened and then used as + arguments to Git. + + Display the command line arguments in the echo area. + + After Git returns some buffers are refreshed: the buffer that was + current when this function was called (if it is a Magit buffer and + still alive), as well as the respective Magit status buffer. + + -- Function: magit-start-git input &rest args + Start Git, prepare for refresh, and return the process object. + + If INPUT is non-nil, it has to be a buffer or the name of an + existing buffer. The buffer content becomes the processes standard + input. + + Option ‘magit-git-executable’ specifies the Git executable and + option ‘magit-git-global-arguments’ specifies constant arguments. + The remaining arguments ARGS specify arguments to Git. They are + flattened before use. + + After Git returns, some buffers are refreshed: the buffer that was + current when this function was called (if it is a Magit buffer and + still alive), as well as the respective Magit status buffer. + Unmodified buffers visiting files that are tracked in the current + repository are reverted if ‘magit-revert-buffers’ is non-nil. + + -- Function: magit-start-process &rest args + Start PROGRAM, prepare for refresh, and return the process object. + + If optional argument INPUT is non-nil, it has to be a buffer or the + name of an existing buffer. The buffer content becomes the + processes standard input. + + The process is started using ‘start-file-process’ and then setup to + use the sentinel ‘magit-process-sentinel’ and the filter + ‘magit-process-filter’. Information required by these functions is + stored in the process object. When this function returns the + process has not started to run yet so it is possible to override + the sentinel and filter. + + After the process returns, ‘magit-process-sentinel’ refreshes the + buffer that was current when ‘magit-start-process’ was called (if + it is a Magit buffer and still alive), as well as the respective + Magit status buffer. Unmodified buffers visiting files that are + tracked in the current repository are reverted if + ‘magit-revert-buffers’ is non-nil. + + -- Variable: magit-this-process + The child process which is about to start. This can be used to + change the filter and sentinel. + + -- Variable: magit-process-raise-error + When this is non-nil, then ‘magit-process-sentinel’ raises an error + if git exits with a non-zero exit status. For debugging purposes. + + +File: magit.info, Node: Section Plumbing, Next: Refreshing Buffers, Prev: Calling Git, Up: Plumbing + +10.2 Section Plumbing +===================== + +* Menu: + +* Creating Sections:: +* Section Selection:: +* Matching Sections:: + + +File: magit.info, Node: Creating Sections, Next: Section Selection, Up: Section Plumbing + +10.2.1 Creating Sections +------------------------ + + -- Macro: magit-insert-section &rest args + Insert a section at point. + + TYPE is the section type, a symbol. Many commands that act on the + current section behave differently depending on that type. Also if + a variable ‘magit-TYPE-section-map’ exists, then use that as the + text-property ‘keymap’ of all text belonging to the section (but + this may be overwritten in subsections). TYPE can also have the + form ‘(eval FORM)’ in which case FORM is evaluated at runtime. + + Optional VALUE is the value of the section, usually a string that + is required when acting on the section. + + When optional HIDE is non-nil collapse the section body by default, + i.e., when first creating the section, but not when refreshing the + buffer. Otherwise, expand it by default. This can be overwritten + using ‘magit-section-set-visibility-hook’. When a section is + recreated during a refresh, then the visibility of predecessor is + inherited and HIDE is ignored (but the hook is still honored). + + BODY is any number of forms that actually insert the section’s + heading and body. Optional NAME, if specified, has to be a symbol, + which is then bound to the struct of the section being inserted. + + Before BODY is evaluated the ‘start’ of the section object is set + to the value of ‘point’ and after BODY was evaluated its ‘end’ is + set to the new value of ‘point’; BODY is responsible for moving + ‘point’ forward. + + If it turns out inside BODY that the section is empty, then + ‘magit-cancel-section’ can be used to abort and remove all traces + of the partially inserted section. This can happen when creating a + section by washing Git’s output and Git didn’t actually output + anything this time around. + + -- Function: magit-insert-heading &rest args + Insert the heading for the section currently being inserted. + + This function should only be used inside ‘magit-insert-section’. + + When called without any arguments, then just set the ‘content’ slot + of the object representing the section being inserted to a marker + at ‘point’. The section should only contain a single line when + this function is used like this. + + When called with arguments ARGS, which have to be strings, then + insert those strings at point. The section should not contain any + text before this happens and afterwards it should again only + contain a single line. If the ‘face’ property is set anywhere + inside any of these strings, then insert all of them unchanged. + Otherwise use the ‘magit-section-heading’ face for all inserted + text. + + The ‘content’ property of the section struct is the end of the + heading (which lasts from ‘start’ to ‘content’) and the beginning + of the body (which lasts from ‘content’ to ‘end’). If the value of + ‘content’ is nil, then the section has no heading and its body + cannot be collapsed. If a section does have a heading then its + height must be exactly one line, including a trailing newline + character. This isn’t enforced; you are responsible for getting it + right. The only exception is that this function does insert a + newline character if necessary. + + -- Function: magit-cancel-section + Cancel the section currently being inserted. This exits the + innermost call to ‘magit-insert-section’ and removes all traces of + what has already happened inside that call. + + -- Function: magit-define-section-jumper sym title &optional value + Define an interactive function to go to section SYM. TITLE is the + displayed title of the section. + + +File: magit.info, Node: Section Selection, Next: Matching Sections, Prev: Creating Sections, Up: Section Plumbing + +10.2.2 Section Selection +------------------------ + + -- Function: magit-current-section + Return the section at point. + + -- Function: magit-region-sections &optional condition multiple + Return a list of the selected sections. + + When the region is active and constitutes a valid section + selection, then return a list of all selected sections. This is + the case when the region begins in the heading of a section and + ends in the heading of the same section or in that of a sibling + section. If optional MULTIPLE is non-nil, then the region cannot + begin and end in the same section. + + When the selection is not valid, then return nil. In this case, + most commands that can act on the selected sections will instead + act on the section at point. + + When the region looks like it would in any other buffer then the + selection is invalid. When the selection is valid then the region + uses the ‘magit-section-highlight’ face. This does not apply to + diffs where things get a bit more complicated, but even here if the + region looks like it usually does, then that’s not a valid + selection as far as this function is concerned. + + If optional CONDITION is non-nil, then the selection not only has + to be valid; all selected sections additionally have to match + CONDITION, or nil is returned. See ‘magit-section-match’ for the + forms CONDITION can take. + + -- Function: magit-region-values &optional condition multiple + Return a list of the values of the selected sections. + + Return the values that themselves would be returned by + ‘magit-region-sections’ (which see). + + +File: magit.info, Node: Matching Sections, Prev: Section Selection, Up: Section Plumbing + +10.2.3 Matching Sections +------------------------ + +‘M-x magit-describe-section-briefly’ + Show information about the section at point. This command is + intended for debugging purposes. + + -- Function: magit-section-ident section + Return an unique identifier for SECTION. The return value has the + form ‘((TYPE . VALUE)...)’. + + -- Function: magit-get-section ident &optional root + Return the section identified by IDENT. IDENT has to be a list as + returned by ‘magit-section-ident’. + + -- Function: magit-section-match condition &optional section + Return ‘t’ if SECTION matches CONDITION. SECTION defaults to the + section at point. If SECTION is not specified and there also is no + section at point, then return ‘nil’. + + CONDITION can take the following forms: + • ‘(CONDITION...)’ + + matches if any of the CONDITIONs matches. + + • ‘[CLASS...]’ + + matches if the section’s class is the same as the first CLASS + or a subclass of that; the section’s parent class matches the + second CLASS; and so on. + + • ‘[* CLASS...]’ + + matches sections that match ‘[CLASS...]’ and also recursively + all their child sections. + + • ‘CLASS’ + + matches if the section’s class is the same as CLASS or a + subclass of that; regardless of the classes of the parent + sections. + + Each CLASS should be a class symbol, identifying a class that + derives from ‘magit-section’. For backward compatibility CLASS can + also be a "type symbol". A section matches such a symbol if the + value of its ‘type’ slot is ‘eq’. If a type symbol has an entry in + ‘magit--section-type-alist’, then a section also matches that type + if its class is a subclass of the class that corresponds to the + type as per that alist. + + Note that it is not necessary to specify the complete section + lineage as printed by ‘magit-describe-section-briefly’, unless of + course you want to be that precise. + + -- Function: magit-section-value-if condition &optional section + If the section at point matches CONDITION, then return its value. + + If optional SECTION is non-nil then test whether that matches + instead. If there is no section at point and SECTION is nil, then + return nil. If the section does not match, then return nil. + + See ‘magit-section-match’ for the forms CONDITION can take. + + -- Function: magit-section-case &rest clauses + Choose among clauses on the type of the section at point. + + Each clause looks like (CONDITION BODY...). The type of the + section is compared against each CONDITION; the BODY forms of the + first match are evaluated sequentially and the value of the last + form is returned. Inside BODY the symbol ‘it’ is bound to the + section at point. If no clause succeeds or if there is no section + at point return nil. + + See ‘magit-section-match’ for the forms CONDITION can take. + Additionally a CONDITION of t is allowed in the final clause and + matches if no other CONDITION match, even if there is no section at + point. + + -- Variable: magit-root-section + The root section in the current buffer. All other sections are + descendants of this section. The value of this variable is set by + ‘magit-insert-section’ and you should never modify it. + + For diff related sections a few additional tools exist. + + -- Function: magit-diff-type &optional section + Return the diff type of SECTION. + + The returned type is one of the symbols ‘staged’, ‘unstaged’, + ‘committed’, or ‘undefined’. This type serves a similar purpose as + the general type common to all sections (which is stored in the + ‘type’ slot of the corresponding ‘magit-section’ struct) but takes + additional information into account. When the SECTION isn’t + related to diffs and the buffer containing it also isn’t a + diff-only buffer, then return nil. + + Currently the type can also be one of ‘tracked’ and ‘untracked’, + but these values are not handled explicitly in every place they + should be. A possible fix could be to just return nil here. + + The section has to be a ‘diff’ or ‘hunk’ section, or a section + whose children are of type ‘diff’. If optional SECTION is nil, + return the diff type for the current section. In buffers whose + major mode is ‘magit-diff-mode’ SECTION is ignored and the type is + determined using other means. In ‘magit-revision-mode’ buffers the + type is always ‘committed’. + + -- Function: magit-diff-scope &optional section strict + Return the diff scope of SECTION or the selected section(s). + + A diff’s "scope" describes what part of a diff is selected, it is a + symbol, one of ‘region’, ‘hunk’, ‘hunks’, ‘file’, ‘files’, or + ‘list’. Do not confuse this with the diff "type", as returned by + ‘magit-diff-type’. + + If optional SECTION is non-nil, then return the scope of that, + ignoring the sections selected by the region. Otherwise return the + scope of the current section, or if the region is active and + selects a valid group of diff related sections, the type of these + sections, i.e., ‘hunks’ or ‘files’. If SECTION (or if the current + section that is nil) is a ‘hunk’ section and the region starts and + ends inside the body of a that section, then the type is ‘region’. + + If optional STRICT is non-nil then return nil if the diff type of + the section at point is ‘untracked’ or the section at point is not + actually a ‘diff’ but a ‘diffstat’ section. + + +File: magit.info, Node: Refreshing Buffers, Next: Conventions, Prev: Section Plumbing, Up: Plumbing + +10.3 Refreshing Buffers +======================= + +All commands that create a new Magit buffer or change what is being +displayed in an existing buffer do so by calling ‘magit-mode-setup’. +Among other things, that function sets the buffer local values of +‘default-directory’ (to the top-level of the repository), +‘magit-refresh-function’, and ‘magit-refresh-args’. + + Buffers are refreshed by calling the function that is the local value +of ‘magit-refresh-function’ (a function named ‘magit-*-refresh-buffer’, +where ‘*’ may be something like ‘diff’) with the value of +‘magit-refresh-args’ as arguments. + + -- Macro: magit-mode-setup buffer switch-func mode refresh-func + &optional refresh-args + This function displays and selects BUFFER, turns on MODE, and + refreshes a first time. + + This function displays and optionally selects BUFFER by calling + ‘magit-mode-display-buffer’ with BUFFER, MODE and SWITCH-FUNC as + arguments. Then it sets the local value of + ‘magit-refresh-function’ to REFRESH-FUNC and that of + ‘magit-refresh-args’ to REFRESH-ARGS. Finally it creates the + buffer content by calling REFRESH-FUNC with REFRESH-ARGS as + arguments. + + All arguments are evaluated before switching to BUFFER. + + -- Function: magit-mode-display-buffer buffer mode &optional + switch-function + This function display BUFFER in some window and select it. BUFFER + may be a buffer or a string, the name of a buffer. The buffer is + returned. + + Unless BUFFER is already displayed in the selected frame, store the + previous window configuration as a buffer local value, so that it + can later be restored by ‘magit-mode-bury-buffer’. + + The buffer is displayed and selected using SWITCH-FUNCTION. If + that is ‘nil’ then ‘pop-to-buffer’ is used if the current buffer’s + major mode derives from ‘magit-mode’. Otherwise ‘switch-to-buffer’ + is used. + + -- Variable: magit-refresh-function + The value of this buffer-local variable is the function used to + refresh the current buffer. It is called with ‘magit-refresh-args’ + as arguments. + + -- Variable: magit-refresh-args + The list of arguments used by ‘magit-refresh-function’ to refresh + the current buffer. ‘magit-refresh-function’ is called with these + arguments. + + The value is usually set using ‘magit-mode-setup’, but in some + cases it’s also useful to provide commands that can change the + value. For example, the ‘magit-diff-refresh’ transient can be used + to change any of the arguments used to display the diff, without + having to specify again which differences should be shown, but + ‘magit-diff-more-context’, ‘magit-diff-less-context’ and + ‘magit-diff-default-context’ change just the ‘-U<N>’ argument. In + both case this is done by changing the value of this variable and + then calling this ‘magit-refresh-function’. + + +File: magit.info, Node: Conventions, Prev: Refreshing Buffers, Up: Plumbing + +10.4 Conventions +================ + +Also see *note Completion and Confirmation::. + +* Menu: + +* Theming Faces:: + + +File: magit.info, Node: Theming Faces, Up: Conventions + +10.4.1 Theming Faces +-------------------- + +The default theme uses blue for local branches, green for remote +branches, and goldenrod (brownish yellow) for tags. When creating a new +theme, you should probably follow that example. If your theme already +uses other colors, then stick to that. + + In older releases these reference faces used to have a background +color and a box around them. The basic default faces no longer do so, +to make Magit buffers much less noisy, and you should follow that +example at least with regards to boxes. (Boxes were used in the past to +work around a conflict between the highlighting overlay and text +property backgrounds. That’s no longer necessary because highlighting +no longer causes other background colors to disappear.) Alternatively +you can keep the background color and/or box, but then have to take +special care to adjust ‘magit-branch-current’ accordingly. By default +it looks mostly like ‘magit-branch-local’, but with a box (by default +the former is the only face that uses a box, exactly so that it sticks +out). If the former also uses a box, then you have to make sure that it +differs in some other way from the latter. + + The most difficult faces to theme are those related to diffs, +headings, highlighting, and the region. There are faces that fall into +all four groups - expect to spend some time getting this right. + + The ‘region’ face in the default theme, in both the light and dark +variants, as well as in many other themes, distributed with Emacs or by +third-parties, is very ugly. It is common to use a background color +that really sticks out, which is ugly but if that were the only problem +then it would be acceptable. Unfortunately many themes also set the +foreground color, which ensures that all text within the region is +readable. Without doing that there might be cases where some foreground +color is too close to the region background color to still be readable. +But it also means that text within the region loses all syntax +highlighting. + + I consider the work that went into getting the ‘region’ face right to +be a good indicator for the general quality of a theme. My +recommendation for the ‘region’ face is this: use a background color +slightly different from the background color of the ‘default’ face, and +do not set the foreground color at all. So for a light theme you might +use a light (possibly tinted) gray as the background color of ‘default’ +and a somewhat darker gray for the background of ‘region’. That should +usually be enough to not collide with the foreground color of any other +face. But if some other faces also set a light gray as background +color, then you should also make sure it doesn’t collide with those (in +some cases it might be acceptable though). + + Magit only uses the ‘region’ face when the region is "invalid" by its +own definition. In a Magit buffer the region is used to either select +multiple sibling sections, so that commands which support it act on all +of these sections instead of just the current section, or to select +lines within a single hunk section. In all other cases, the section is +considered invalid and Magit won’t act on it. But such invalid sections +happen, either because the user has not moved point enough yet to make +it valid or because she wants to use a non-magit command to act on the +region, e.g., ‘kill-region’. + + So using the regular ‘region’ face for invalid sections is a feature. +It tells the user that Magit won’t be able to act on it. It’s +acceptable if that face looks a bit odd and even (but less so) if it +collides with the background colors of section headings and other things +that have a background color. + + Magit highlights the current section. If a section has subsections, +then all of them are highlighted. This is done using faces that have +"highlight" in their names. For most sections, +‘magit-section-highlight’ is used for both the body and the heading. +Like the ‘region’ face, it should only set the background color to +something similar to that of ‘default’. The highlight background color +must be different from both the ‘region’ background color and the +‘default’ background color. + + For diff related sections Magit uses various faces to highlight +different parts of the selected section(s). Note that hunk headings, +unlike all other section headings, by default have a background color, +because it is useful to have very visible separators between hunks. +That face ‘magit-diff-hunk-heading’, should be different from both +‘magit-diff-hunk-heading-highlight’ and ‘magit-section-highlight’, as +well as from ‘magit-diff-context’ and ‘magit-diff-context-highlight’. +By default we do that by changing the foreground color. Changing the +background color would lead to complications, and there are already +enough we cannot get around. (Also note that it is generally a good +idea for section headings to always be bold, but only for sections that +have subsections). + + When there is a valid region selecting diff-related sibling sections, +i.e., multiple files or hunks, then the bodies of all these sections use +the respective highlight faces, but additionally the headings instead +use one of the faces ‘magit-diff-file-heading-selection’ or +‘magit-diff-hunk-heading-selection’. These faces have to be different +from the regular highlight variants to provide explicit visual +indication that the region is active. + + When theming diff related faces, start by setting the option +‘magit-diff-refine-hunk’ to ‘all’. You might personally prefer to only +refine the current hunk or not use hunk refinement at all, but some of +the users of your theme want all hunks to be refined, so you have to +cater to that. + + (Also turn on ‘magit-diff-highlight-indentation’, +‘magit-diff-highlight-trailing’, and ‘magit-diff-paint-whitespace’; and +insert some whitespace errors into the code you use for testing.) + + For added lines you have to adjust three faces: ‘magit-diff-added’, +‘magit-diff-added-highlight’, and ‘diff-refined-added’. Make sure that +the latter works well with both of the former, as well as ‘smerge-other’ +and ‘diff-added’. Then do the same for the removed lines, context +lines, lines added by us, and lines added by them. Also make sure the +respective added, removed, and context faces use approximately the same +saturation for both the highlighted and unhighlighted variants. Also +make sure the file and diff headings work nicely with context lines +(e.g., make them look different). Line faces should set both the +foreground and the background color. For example, for added lines use +two different greens. + + It’s best if the foreground color of both the highlighted and the +unhighlighted variants are the same, so you will need to have to find a +color that works well on the highlight and unhighlighted background, the +refine background, and the highlight context background. When there is +an hunk internal region, then the added- and removed-lines background +color is used only within that region. Outside the region the +highlighted context background color is used. This makes it easier to +see what is being staged. With an hunk internal region the hunk heading +is shown using ‘magit-diff-hunk-heading-selection’, and so are the thin +lines that are added around the lines that fall within the region. The +background color of that has to be distinct enough from the various +other involved background colors. + + Nobody said this would be easy. If your theme restricts itself to a +certain set of colors, then you should make an exception here. +Otherwise it would be impossible to make the diffs look good in each and +every variation. Actually you might want to just stick to the default +definitions for these faces. You have been warned. Also please note +that if you do not get this right, this will in some cases look to users +like bugs in Magit - so please do it right or not at all. + + +File: magit.info, Node: FAQ, Next: Debugging Tools, Prev: Plumbing, Up: Top + +Appendix A FAQ +************** + +The next two nodes lists frequently asked questions. For a list of +frequently *and recently* asked questions, i.e., questions that haven’t +made it into the manual yet, see +<https://github.com/magit/magit/wiki/FAQ>. + + Please also see *note Debugging Tools::. + +* Menu: + +* FAQ - How to ...?:: +* FAQ - Issues and Errors:: + + +File: magit.info, Node: FAQ - How to ...?, Next: FAQ - Issues and Errors, Up: FAQ + +A.1 FAQ - How to ...? +===================== + +* Menu: + +* How to pronounce Magit?:: +* How to show git's output?:: +* How to install the gitman info manual?:: +* How to show diffs for gpg-encrypted files?:: +* How does branching and pushing work?:: +* Should I disable VC?:: + + +File: magit.info, Node: How to pronounce Magit?, Next: How to show git's output?, Up: FAQ - How to ...? + +A.1.1 How to pronounce Magit? +----------------------------- + +Either ‘mu[m's] git’ or ‘magi{c => t}’ is fine. + + The slogan is "It’s Magit! The magical Git client", so it makes +sense to pronounce Magit like magic, while taking into account that C +and T do not sound the same. + + The German "Magie" is not pronounced the same as the English "magic", +so if you speak German then you can use the above rationale to justify +using the former pronunciation; ‘Mag{ie => it}’. + + You can also choose to use the former pronunciation just because you +like it better. + + Also see <https://magit.vc/assets/videos/magic.mp4>. Also see +<https://emacs.stackexchange.com/questions/13696>. + + +File: magit.info, Node: How to show git's output?, Next: How to install the gitman info manual?, Prev: How to pronounce Magit?, Up: FAQ - How to ...? + +A.1.2 How to show git’s output? +------------------------------- + +To show the output of recently run git commands, press ‘$’ (or, if that +isn’t available, ‘M-x magit-process-buffer’). This will show a buffer +containing a section per git invocation; as always press ‘TAB’ to expand +or collapse them. + + By default, git’s output is only inserted into the process buffer if +it is run for side-effects. When the output is consumed in some way, +also inserting it into the process buffer would be too expensive. For +debugging purposes, it’s possible to do so anyway by setting +‘magit-git-debug’ to ‘t’. + + +File: magit.info, Node: How to install the gitman info manual?, Next: How to show diffs for gpg-encrypted files?, Prev: How to show git's output?, Up: FAQ - How to ...? + +A.1.3 How to install the gitman info manual? +-------------------------------------------- + +Git’s manpages can be exported as an info manual called ‘gitman’. +Magit’s own info manual links to nodes in that manual instead of the +actual manpages because Info doesn’t support linking to manpages. + + Unfortunately some distributions do not install the ‘gitman’ manual +by default and you will have to install a separate documentation package +to get it. + + Magit patches Info adding the ability to visit links to the ‘gitman’ +Info manual by instead viewing the respective manpage. If you prefer +that approach, then set the value of ‘magit-view-git-manual-method’ to +one of the supported packages ‘man’ or ‘woman’, e.g.: + + (setq magit-view-git-manual-method 'man) + + +File: magit.info, Node: How to show diffs for gpg-encrypted files?, Next: How does branching and pushing work?, Prev: How to install the gitman info manual?, Up: FAQ - How to ...? + +A.1.4 How to show diffs for gpg-encrypted files? +------------------------------------------------ + +Git supports showing diffs for encrypted files, but has to be told to do +so. Since Magit just uses Git to get the diffs, configuring Git also +affects the diffs displayed inside Magit. + + git config --global diff.gpg.textconv "gpg --no-tty --decrypt" + echo "*.gpg filter=gpg diff=gpg" > .gitattributes + + +File: magit.info, Node: How does branching and pushing work?, Next: Should I disable VC?, Prev: How to show diffs for gpg-encrypted files?, Up: FAQ - How to ...? + +A.1.5 How does branching and pushing work? +------------------------------------------ + +Please see *note Branching:: and +<https://emacsair.me/2016/01/17/magit-2.4> + + +File: magit.info, Node: Should I disable VC?, Prev: How does branching and pushing work?, Up: FAQ - How to ...? + +A.1.6 Should I disable VC? +-------------------------- + +If you don’t use VC (the built-in version control interface) then you +might be tempted to disable it, not least because we used to recommend +that you do that. + + We no longer recommend that you disable VC. Doing so would break +useful third-party packages (such as ‘diff-hl’), which depend on VC +being enabled. + + If you choose to disable VC anyway, then you can do so by changing +the value of ‘vc-handled-backends’. + + +File: magit.info, Node: FAQ - Issues and Errors, Prev: FAQ - How to ...?, Up: FAQ + +A.2 FAQ - Issues and Errors +=========================== + +* Menu: + +* Magit is slow:: +* I changed several thousand files at once and now Magit is unusable:: +* I am having problems committing:: +* I am using MS Windows and cannot push with Magit:: +* I am using macOS and SOMETHING works in shell, but not in Magit: I am using macOS and SOMETHING works in shell but not in Magit. +* Expanding a file to show the diff causes it to disappear:: +* Point is wrong in the COMMIT_EDITMSG buffer:: +* The mode-line information isn't always up-to-date:: +* A branch and tag sharing the same name breaks SOMETHING:: +* My Git hooks work on the command-line but not inside Magit:: +* git-commit-mode isn't used when committing from the command-line:: +* Point ends up inside invisible text when jumping to a file-visiting buffer:: +* I am no longer able to save popup defaults:: + + +File: magit.info, Node: Magit is slow, Next: I changed several thousand files at once and now Magit is unusable, Up: FAQ - Issues and Errors + +A.2.1 Magit is slow +------------------- + +See *note Performance:: and *note I changed several thousand files at +once and now Magit is unusable::. + + +File: magit.info, Node: I changed several thousand files at once and now Magit is unusable, Next: I am having problems committing, Prev: Magit is slow, Up: FAQ - Issues and Errors + +A.2.2 I changed several thousand files at once and now Magit is unusable +------------------------------------------------------------------------ + +Magit is currently not expected to work well under such conditions. It +sure would be nice if it did. Reaching satisfactory performance under +such conditions will require some heavy refactoring. This is no small +task but I hope to eventually find the time to make it happen. + + But for now we recommend you use the command line to complete this +one commit. Also see *note Performance::. + + +File: magit.info, Node: I am having problems committing, Next: I am using MS Windows and cannot push with Magit, Prev: I changed several thousand files at once and now Magit is unusable, Up: FAQ - Issues and Errors + +A.2.3 I am having problems committing +------------------------------------- + +That likely means that Magit is having problems finding an appropriate +emacsclient executable. See *note (with-editor)Configuring +With-Editor:: and *note (with-editor)Debugging::. + + +File: magit.info, Node: I am using MS Windows and cannot push with Magit, Next: I am using macOS and SOMETHING works in shell but not in Magit, Prev: I am having problems committing, Up: FAQ - Issues and Errors + +A.2.4 I am using MS Windows and cannot push with Magit +------------------------------------------------------ + +It’s almost certain that Magit is only incidental to this issue. It is +much more likely that this is a configuration issue, even if you can +push on the command line. + + Detailed setup instructions can be found at +<https://github.com/magit/magit/wiki/Pushing-with-Magit-from-Windows>. + + +File: magit.info, Node: I am using macOS and SOMETHING works in shell but not in Magit, Next: Expanding a file to show the diff causes it to disappear, Prev: I am using MS Windows and cannot push with Magit, Up: FAQ - Issues and Errors + +A.2.5 I am using macOS and SOMETHING works in shell, but not in Magit +--------------------------------------------------------------------- + +This usually occurs because Emacs doesn’t have the same environment +variables as your shell. Try installing and configuring +<https://github.com/purcell/exec-path-from-shell>. By default it +synchronizes ‘$PATH’, which helps Magit find the same ‘git’ as the one +you are using on the shell. + + If SOMETHING is "passphrase caching with gpg-agent for commit and/or +tag signing", then you’ll also need to synchronize ‘$GPG_AGENT_INFO’. + + +File: magit.info, Node: Expanding a file to show the diff causes it to disappear, Next: Point is wrong in the COMMIT_EDITMSG buffer, Prev: I am using macOS and SOMETHING works in shell but not in Magit, Up: FAQ - Issues and Errors + +A.2.6 Expanding a file to show the diff causes it to disappear +-------------------------------------------------------------- + +This is probably caused by a customization of a ‘diff.*’ Git variable. +You probably set that variable for a reason, and should therefore only +undo that setting in Magit by customizing ‘magit-git-global-arguments’. + + +File: magit.info, Node: Point is wrong in the COMMIT_EDITMSG buffer, Next: The mode-line information isn't always up-to-date, Prev: Expanding a file to show the diff causes it to disappear, Up: FAQ - Issues and Errors + +A.2.7 Point is wrong in the ‘COMMIT_EDITMSG’ buffer +--------------------------------------------------- + +Neither Magit nor ‘git-commit.el’ fiddle with point in the buffer used +to write commit messages, so something else must be doing it. + + You have probably globally enabled a mode which restores point in +file-visiting buffers. It might be a bit surprising, but when you write +a commit message, then you are actually editing a file. + + So you have to figure out which package is doing it. ‘saveplace’, +‘pointback’, and ‘session’ are likely candidates. These snippets might +help: + + (setq session-name-disable-regexp "\\(?:\\`'\\.git/[A-Z_]+\\'\\)") + + (with-eval-after-load 'pointback + (lambda () + (when (or git-commit-mode git-rebase-mode) + (pointback-mode -1)))) + + +File: magit.info, Node: The mode-line information isn't always up-to-date, Next: A branch and tag sharing the same name breaks SOMETHING, Prev: Point is wrong in the COMMIT_EDITMSG buffer, Up: FAQ - Issues and Errors + +A.2.8 The mode-line information isn’t always up-to-date +------------------------------------------------------- + +Magit is not responsible for the version control information that is +being displayed in the mode-line and looks something like ‘Git-master’. +The built-in "Version Control" package, also known as "VC", updates that +information, and can be told to do so more often: + + (setq auto-revert-check-vc-info t) + + But doing so isn’t good for performance. For more (overly +optimistic) information see *note (emacs)VC Mode Line::. + + If you don’t really care about seeing this information in the +mode-line, but just don’t want to see _incorrect_ information, then +consider simply not displaying it in the mode-line: + + (setq-default mode-line-format + (delete '(vc-mode vc-mode) mode-line-format)) + + +File: magit.info, Node: A branch and tag sharing the same name breaks SOMETHING, Next: My Git hooks work on the command-line but not inside Magit, Prev: The mode-line information isn't always up-to-date, Up: FAQ - Issues and Errors + +A.2.9 A branch and tag sharing the same name breaks SOMETHING +------------------------------------------------------------- + +Or more generally, ambiguous refnames break SOMETHING. + + Magit assumes that refs are named non-ambiguously across the +"refs/heads/", "refs/tags/", and "refs/remotes/" namespaces (i.e., all +the names remain unique when those prefixes are stripped). We consider +ambiguous refnames unsupported and recommend that you use a +non-ambiguous naming scheme. However, if you do work with a repository +that has ambiguous refnames, please report any issues you encounter, so +that we can investigate whether there is a simple fix. + + +File: magit.info, Node: My Git hooks work on the command-line but not inside Magit, Next: git-commit-mode isn't used when committing from the command-line, Prev: A branch and tag sharing the same name breaks SOMETHING, Up: FAQ - Issues and Errors + +A.2.10 My Git hooks work on the command-line but not inside Magit +----------------------------------------------------------------- + +When Magit calls ‘git’ it adds a few global arguments including +‘--literal-pathspecs’ and the ‘git’ process started by Magit then passes +that setting on to other ‘git’ process it starts itself. It does so by +setting the environment variable ‘GIT_LITERAL_PATHSPECS’, not by calling +subprocesses with the ‘--literal-pathspecs’ argument. You can therefore +override this setting in hook scripts using ‘unset +GIT_LITERAL_PATHSPECS’. + + +File: magit.info, Node: git-commit-mode isn't used when committing from the command-line, Next: Point ends up inside invisible text when jumping to a file-visiting buffer, Prev: My Git hooks work on the command-line but not inside Magit, Up: FAQ - Issues and Errors + +A.2.11 ‘git-commit-mode’ isn’t used when committing from the command-line +------------------------------------------------------------------------- + +The reason for this is that ‘git-commit.el’ has not been loaded yet +and/or that the server has not been started yet. These things have +always already been taken care of when you commit from Magit because in +order to do so, Magit has to be loaded and doing that involves loading +‘git-commit’ and starting the server. + + If you want to commit from the command-line, then you have to take +care of these things yourself. Your ‘init.el’ file should contain: + + (require 'git-commit) + (server-mode) + + Instead of ‘(require ’git-commit)‘ you may also use: + + (load "/path/to/magit-autoloads.el") + + You might want to do that because loading ‘git-commit’ causes large +parts of Magit to be loaded. + + There are also some variations of ‘(server-mode)’ that you might want +to try. Personally I use: + + (use-package server + :config (or (server-running-p) (server-mode))) + + Now you can use: + + $ emacs& + $ EDITOR=emacsclient git commit + + However you cannot use: + + $ killall emacs + $ EDITOR="emacsclient --alternate-editor emacs" git commit + + This will actually end up using ‘emacs’, not ‘emacsclient’. If you +do this, then you can still edit the commit message but +‘git-commit-mode’ won’t be used and you have to exit ‘emacs’ to finish +the process. + + Tautology ahead. If you want to be able to use ‘emacsclient’ to +connect to a running ‘emacs’ instance, even though no ‘emacs’ instance +is running, then you cannot use ‘emacsclient’ directly. + + Instead you have to create a script that does something like this: + + Try to use ‘emacsclient’ (without using ‘--alternate-editor’). If +that succeeds, do nothing else. Otherwise start ‘emacs &’ (and +‘init.el’ must call ‘server-start’) and try to use ‘emacsclient’ again. + + +File: magit.info, Node: Point ends up inside invisible text when jumping to a file-visiting buffer, Next: I am no longer able to save popup defaults, Prev: git-commit-mode isn't used when committing from the command-line, Up: FAQ - Issues and Errors + +A.2.12 Point ends up inside invisible text when jumping to a file-visiting buffer +--------------------------------------------------------------------------------- + +This can happen when you type ‘RET’ on a hunk to visit the respective +file at the respective position. One solution to this problem is to use +‘global-reveal-mode’. It makes sure that text around point is always +visible. If that is too drastic for your taste, then you may instead +use ‘magit-diff-visit-file-hook’ to reveal the text, possibly using +‘reveal-post-command’ or for Org buffers ‘org-reveal’. + + +File: magit.info, Node: I am no longer able to save popup defaults, Prev: Point ends up inside invisible text when jumping to a file-visiting buffer, Up: FAQ - Issues and Errors + +A.2.13 I am no longer able to save popup defaults +------------------------------------------------- + +Magit used to use Magit-Popup to implement the transient popup menus. +Now it used Transient instead, which is Magit-Popup’s successor. + + In the older Magit-Popup menus, it was possible to save user settings +(e.g., setting the gpg signing key for commits) by using ‘C-c C-c’ in +the popup buffer. This would dismiss the popup, but save the settings +as the defaults for future popups. + + When switching to Transient menus, this functionality is now +available via ‘C-x C-s’ instead; the ‘C-x’ prefix has other options as +well when using Transient, which will be displayed when it is typed. +See <https://magit.vc/manual/transient/Saving-Values.html#Saving-Values> +for more details. + + +File: magit.info, Node: Debugging Tools, Next: Keystroke Index, Prev: FAQ, Up: Top + +B Debugging Tools +***************** + +Magit and its dependencies provide a few debugging tools, and we +appreciate it very much if you use those tools before reporting an +issue. Please include all relevant output when reporting an issue. + +‘M-x magit-version’ + This command shows the currently used versions of Magit, Git, and + Emacs in the echo area. Non-interactively this just returns the + Magit version. + +‘M-x magit-emacs-Q-command’ + This command shows a debugging shell command in the echo area and + adds it to the kill ring. Paste that command into a shell and run + it. + + This shell command starts ‘emacs’ with only ‘magit’ and its + dependencies loaded. Neither your configuration nor other + installed packages are loaded. This makes it easier to determine + whether some issue lays with Magit or something else. + + If you run Magit from its Git repository, then you should be able + to use ‘make emacs-Q’ instead of the output of this command. + +‘M-x magit-toggle-git-debug’ + This command toggles whether additional git errors are reported. + + Magit basically calls git for one of these two reasons: for + side-effects or to do something with its standard output. + + When git is run for side-effects then its output, including error + messages, go into the process buffer which is shown when using ‘$’. + + When git’s output is consumed in some way, then it would be too + expensive to also insert it into this buffer, but when this option + is non-nil and git returns with a non-zero exit status, then at + least its standard error is inserted into this buffer. + + This is only intended for debugging purposes. Do not enable this + permanently, that would negatively affect performance. Also note + that just because git exits with a non-zero exit status and prints + an error message that usually doesn’t mean that it is an error as + far as Magit is concerned, which is another reason we usually hide + these error messages. Whether some error message is relevant in + the context of some unexpected behavior has to be judged on a case + by case basis. + +‘M-x magit-toggle-verbose-refresh’ + This command toggles whether Magit refreshes buffers verbosely. + Enabling this helps figuring out which sections are bottlenecks. + The additional output can be found in the ‘*Messages*’ buffer. + +‘M-x magit-debug-git-executable’ + This command displays a buffer containing information about the + available and used ‘git’ executable(s), and can be useful when + investigating ‘exec-path’ issues. + + Also see *note Git Executable::. + +‘M-x with-editor-debug’ + This command displays a buffer containing information about the + available and used ‘emacsclient’ executable(s), and can be useful + when investigating why Magit (or rather ‘with-editor’) cannot find + an appropriate ‘emacsclient’ executable. + + Also see *note (with-editor)Debugging::. + +Please also see *note FAQ::. + + +File: magit.info, Node: Keystroke Index, Next: Function and Command Index, Prev: Debugging Tools, Up: Top + +Appendix C Keystroke Index +************************** + + +* Menu: + +* !: Running Git Manually. + (line 13) +* ! !: Running Git Manually. + (line 17) +* ! a: Running Git Manually. + (line 53) +* ! b: Running Git Manually. + (line 56) +* ! g: Running Git Manually. + (line 59) +* ! k: Running Git Manually. + (line 50) +* ! m: Running Git Manually. + (line 62) +* ! p: Running Git Manually. + (line 25) +* ! s: Running Git Manually. + (line 34) +* ! S: Running Git Manually. + (line 38) +* $: Viewing Git Output. (line 17) +* +: Log Buffer. (line 64) +* + <1>: Refreshing Diffs. (line 65) +* -: Log Buffer. (line 67) +* - <1>: Refreshing Diffs. (line 62) +* 0: Refreshing Diffs. (line 68) +* 1: Section Visibility. (line 39) +* 2: Section Visibility. (line 39) +* 3: Section Visibility. (line 39) +* 4: Section Visibility. (line 39) +* 5: Repository List. (line 115) +* :: Running Git Manually. + (line 25) +* =: Log Buffer. (line 59) +* >: Sparse checkouts. (line 17) +* > a: Sparse checkouts. (line 39) +* > d: Sparse checkouts. (line 50) +* > e: Sparse checkouts. (line 21) +* > r: Sparse checkouts. (line 44) +* > s: Sparse checkouts. (line 33) +* ^: Section Movement. (line 28) +* a: Applying. (line 34) +* A: Cherry Picking. (line 9) +* A A: Cherry Picking. (line 17) +* A a: Cherry Picking. (line 23) +* A A <1>: Cherry Picking. (line 85) +* A a <1>: Cherry Picking. (line 91) +* A d: Cherry Picking. (line 51) +* A h: Cherry Picking. (line 40) +* A n: Cherry Picking. (line 62) +* A s: Cherry Picking. (line 72) +* A s <1>: Cherry Picking. (line 88) +* B: Bisecting. (line 9) +* b: Blaming. (line 115) +* b <1>: Branch Commands. (line 13) +* b <2>: Editing Rebase Sequences. + (line 70) +* B B: Bisecting. (line 16) +* B b: Bisecting. (line 32) +* b b: Branch Commands. (line 47) +* b C: Branch Commands. (line 31) +* b c: Branch Commands. (line 63) +* B g: Bisecting. (line 36) +* B k: Bisecting. (line 46) +* b k: Branch Commands. (line 138) +* b l: Branch Commands. (line 69) +* B m: Bisecting. (line 40) +* b m: Branch Commands. (line 149) +* b n: Branch Commands. (line 54) +* B r: Bisecting. (line 51) +* B s: Bisecting. (line 26) +* b s: Branch Commands. (line 91) +* b S: Branch Commands. (line 118) +* b x: Branch Commands. (line 123) +* c: Blaming. (line 141) +* C: Cloning Repository. (line 20) +* c <1>: Initiating a Commit. (line 9) +* c <2>: Editing Rebase Sequences. + (line 59) +* C >: Cloning Repository. (line 38) +* c a: Initiating a Commit. (line 18) +* c A: Initiating a Commit. (line 59) +* C b: Cloning Repository. (line 44) +* C C: Cloning Repository. (line 28) +* c c: Initiating a Commit. (line 14) +* C d: Cloning Repository. (line 55) +* C e: Cloning Repository. (line 61) +* c e: Initiating a Commit. (line 21) +* c f: Initiating a Commit. (line 39) +* c F: Initiating a Commit. (line 46) +* C m: Cloning Repository. (line 48) +* C s: Cloning Repository. (line 32) +* c s: Initiating a Commit. (line 49) +* c S: Initiating a Commit. (line 56) +* c w: Initiating a Commit. (line 30) +* C-<return>: Visiting Files and Blobs from a Diff. + (line 50) +* C-<tab>: Section Visibility. (line 14) +* C-c C-a: Commit Pseudo Headers. + (line 16) +* C-c C-b: Log Buffer. (line 20) +* C-c C-b <1>: Refreshing Diffs. (line 84) +* C-c C-c: Select from Log. (line 21) +* C-c C-c <1>: Editing Commit Messages. + (line 18) +* C-c C-c <2>: Editing Rebase Sequences. + (line 7) +* C-c C-d: Refreshing Diffs. (line 75) +* C-c C-d <1>: Editing Commit Messages. + (line 54) +* C-c C-e: Commands Available in Diffs. + (line 24) +* C-c C-f: Log Buffer. (line 23) +* C-c C-f <1>: Refreshing Diffs. (line 87) +* C-c C-i: Commit Pseudo Headers. + (line 13) +* C-c C-k: Select from Log. (line 26) +* C-c C-k <1>: Editing Commit Messages. + (line 22) +* C-c C-k <2>: Editing Rebase Sequences. + (line 11) +* C-c C-n: Log Buffer. (line 26) +* C-c C-o: Commit Pseudo Headers. + (line 28) +* C-c C-p: Commit Pseudo Headers. + (line 31) +* C-c C-r: Commit Pseudo Headers. + (line 19) +* C-c C-s: Commit Pseudo Headers. + (line 22) +* C-c C-t: Commands Available in Diffs. + (line 15) +* C-c C-t <1>: Commit Pseudo Headers. + (line 25) +* C-c C-w: Using the Revision Stack. + (line 7) +* C-c f: Commands for Buffers Visiting Files. + (line 52) +* C-c f , c: Commands for Buffers Visiting Files. + (line 52) +* C-c f , k: Commands for Buffers Visiting Files. + (line 52) +* C-c f , r: Commands for Buffers Visiting Files. + (line 52) +* C-c f , x: Commands for Buffers Visiting Files. + (line 52) +* C-c f B: Blaming. (line 28) +* C-c f b: Blaming. (line 28) +* C-c f B <1>: Commands for Buffers Visiting Files. + (line 52) +* C-c f b <1>: Commands for Buffers Visiting Files. + (line 52) +* C-c f B b: Blaming. (line 28) +* C-c f B e: Blaming. (line 28) +* C-c f B f: Blaming. (line 28) +* C-c f B q: Blaming. (line 28) +* C-c f B r: Blaming. (line 28) +* C-c f c: Commands for Buffers Visiting Files. + (line 52) +* C-c f D: Commands for Buffers Visiting Files. + (line 52) +* C-c f d: Commands for Buffers Visiting Files. + (line 52) +* C-c f e: Blaming. (line 28) +* C-c f e <1>: Commands for Buffers Visiting Files. + (line 52) +* C-c f f: Blaming. (line 28) +* C-c f f <1>: Commands for Buffers Visiting Files. + (line 52) +* C-c f g: Commands for Buffers Visiting Files. + (line 52) +* C-c f G: Commands for Buffers Visiting Files. + (line 52) +* C-c f L: Commands for Buffers Visiting Files. + (line 52) +* C-c f l: Commands for Buffers Visiting Files. + (line 52) +* C-c f M: Commands for Buffers Visiting Files. + (line 52) +* C-c f m: Commands for Buffers Visiting Files. + (line 52) +* C-c f n: Commands for Buffers Visiting Files. + (line 52) +* C-c f p: Commands for Buffers Visiting Files. + (line 52) +* C-c f q: Blaming. (line 28) +* C-c f q <1>: Commands for Buffers Visiting Files. + (line 52) +* C-c f r: Blaming. (line 28) +* C-c f r <1>: Commands for Buffers Visiting Files. + (line 52) +* C-c f s: Commands for Buffers Visiting Files. + (line 52) +* C-c f s <1>: Commands for Buffers Visiting Files. + (line 52) +* C-c f t: Commands for Buffers Visiting Files. + (line 52) +* C-c f u: Commands for Buffers Visiting Files. + (line 52) +* C-c f u <1>: Commands for Buffers Visiting Files. + (line 52) +* C-c f v: Commands for Buffers Visiting Files. + (line 52) +* C-c f V: Commands for Buffers Visiting Files. + (line 52) +* C-c g: Transient Commands. (line 20) +* C-c M-g: Commands for Buffers Visiting Files. + (line 58) +* C-c M-g , c: Commands for Buffers Visiting Files. + (line 86) +* C-c M-g , k: Commands for Buffers Visiting Files. + (line 82) +* C-c M-g , r: Commands for Buffers Visiting Files. + (line 78) +* C-c M-g , x: Commands for Buffers Visiting Files. + (line 74) +* C-c M-g B: Blaming. (line 34) +* C-c M-g b: Blaming. (line 45) +* C-c M-g B <1>: Commands for Buffers Visiting Files. + (line 137) +* C-c M-g B b: Blaming. (line 45) +* C-c M-g B e: Blaming. (line 76) +* C-c M-g B f: Blaming. (line 68) +* C-c M-g B q: Blaming. (line 87) +* C-c M-g B r: Blaming. (line 60) +* C-c M-g c: Commands for Buffers Visiting Files. + (line 176) +* C-c M-g D: Commands for Buffers Visiting Files. + (line 91) +* C-c M-g d: Commands for Buffers Visiting Files. + (line 101) +* C-c M-g e: Blaming. (line 76) +* C-c M-g e <1>: Commands for Buffers Visiting Files. + (line 182) +* C-c M-g f: Blaming. (line 68) +* C-c M-g g: Commands for Buffers Visiting Files. + (line 166) +* C-c M-g G: Commands for Buffers Visiting Files. + (line 172) +* C-c M-g L: Commands for Buffers Visiting Files. + (line 109) +* C-c M-g l: Commands for Buffers Visiting Files. + (line 119) +* C-c M-g M: Commands for Buffers Visiting Files. + (line 132) +* C-c M-g n: Commands for Buffers Visiting Files. + (line 153) +* C-c M-g p: Commands for Buffers Visiting Files. + (line 149) +* C-c M-g q: Blaming. (line 87) +* C-c M-g r: Blaming. (line 60) +* C-c M-g s: Commands for Buffers Visiting Files. + (line 63) +* C-c M-g s <1>: Commands for Buffers Visiting Files. + (line 63) +* C-c M-g t: Commands for Buffers Visiting Files. + (line 129) +* C-c M-g u: Commands for Buffers Visiting Files. + (line 69) +* C-c M-g u <1>: Commands for Buffers Visiting Files. + (line 69) +* C-c M-g v: Commands for Buffers Visiting Files. + (line 156) +* C-c M-g V: Commands for Buffers Visiting Files. + (line 160) +* C-c M-i: Commit Pseudo Headers. + (line 35) +* C-c M-s: Editing Commit Messages. + (line 33) +* C-c TAB: Section Visibility. (line 14) +* C-w: Common Commands. (line 22) +* C-x g: Status Buffer. (line 23) +* C-x M-g: Transient Commands. (line 20) +* C-x u: Editing Rebase Sequences. + (line 77) +* d: Diffing. (line 22) +* D: Refreshing Diffs. (line 16) +* d c: Diffing. (line 63) +* d d: Diffing. (line 27) +* D f: Refreshing Diffs. (line 45) +* D F: Refreshing Diffs. (line 49) +* D g: Refreshing Diffs. (line 21) +* d p: Diffing. (line 56) +* d r: Diffing. (line 30) +* D r: Refreshing Diffs. (line 41) +* d s: Diffing. (line 48) +* D s: Refreshing Diffs. (line 25) +* d t: Diffing. (line 67) +* D t: Refreshing Diffs. (line 38) +* d u: Diffing. (line 53) +* d w: Diffing. (line 43) +* D w: Refreshing Diffs. (line 31) +* DEL: Log Buffer. (line 50) +* DEL <1>: Commands Available in Diffs. + (line 56) +* DEL <2>: Blaming. (line 103) +* DEL <3>: Editing Rebase Sequences. + (line 25) +* e: Ediffing. (line 10) +* E: Ediffing. (line 21) +* e <1>: Editing Rebase Sequences. + (line 46) +* E c: Ediffing. (line 100) +* E i: Ediffing. (line 94) +* E m: Ediffing. (line 33) +* E M: Ediffing. (line 48) +* E r: Ediffing. (line 25) +* E s: Ediffing. (line 87) +* E t: Ediffing. (line 79) +* E u: Ediffing. (line 91) +* E w: Ediffing. (line 97) +* E z: Ediffing. (line 103) +* f: Repository List. (line 111) +* f <1>: Editing Rebase Sequences. + (line 52) +* f <2>: Fetching. (line 10) +* F: Pulling. (line 10) +* f a: Fetching. (line 45) +* f C: Branch Commands. (line 31) +* F C: Branch Commands. (line 31) +* f e: Fetching. (line 34) +* F e: Pulling. (line 28) +* f m: Fetching. (line 48) +* f o: Fetching. (line 37) +* f p: Fetching. (line 15) +* F p: Pulling. (line 14) +* f r: Fetching. (line 41) +* f u: Fetching. (line 22) +* F u: Pulling. (line 21) +* g: Automatic Refreshing of Magit Buffers. + (line 26) +* G: Automatic Refreshing of Magit Buffers. + (line 34) +* H: Section Types and Values. + (line 14) +* I: Creating Repository. (line 7) +* j: Log Buffer. (line 31) +* j <1>: Commands Available in Diffs. + (line 43) +* k: Viewing Git Output. (line 24) +* k <1>: Applying. (line 40) +* k <2>: Editing Rebase Sequences. + (line 56) +* k <3>: Stashing. (line 118) +* l: Logging. (line 28) +* L: Refreshing Logs. (line 12) +* L <1>: Log Buffer. (line 7) +* L <2>: Log Margin. (line 52) +* l <1>: Editing Rebase Sequences. + (line 94) +* l a: Logging. (line 59) +* l b: Logging. (line 56) +* L d: Log Margin. (line 66) +* L g: Refreshing Logs. (line 17) +* l h: Logging. (line 38) +* l H: Reflog. (line 18) +* l l: Logging. (line 33) +* l L: Logging. (line 53) +* L L: Refreshing Logs. (line 34) +* L L <1>: Log Margin. (line 60) +* L l: Log Margin. (line 63) +* l o: Logging. (line 47) +* l O: Reflog. (line 15) +* l r: Reflog. (line 12) +* L s: Refreshing Logs. (line 21) +* l u: Logging. (line 41) +* L w: Refreshing Logs. (line 27) +* m: Repository List. (line 105) +* m <1>: Merging. (line 10) +* M: Remote Commands. (line 14) +* m a: Merging. (line 42) +* m a <1>: Merging. (line 91) +* M a: Remote Commands. (line 48) +* M C: Remote Commands. (line 32) +* m e: Merging. (line 30) +* m i: Merging. (line 54) +* M k: Remote Commands. (line 60) +* m m: Merging. (line 18) +* m m <1>: Merging. (line 86) +* m n: Merging. (line 36) +* m p: Merging. (line 75) +* M p: Remote Commands. (line 63) +* M P: Remote Commands. (line 67) +* M r: Remote Commands. (line 52) +* m s: Merging. (line 67) +* M u: Remote Commands. (line 56) +* M-1: Section Visibility. (line 45) +* M-2: Section Visibility. (line 45) +* M-3: Section Visibility. (line 45) +* M-4: Section Visibility. (line 45) +* M-<tab>: Section Visibility. (line 29) +* M-n: Section Movement. (line 24) +* M-n <1>: Editing Commit Messages. + (line 41) +* M-n <2>: Editing Rebase Sequences. + (line 40) +* M-p: Section Movement. (line 19) +* M-p <1>: Editing Commit Messages. + (line 36) +* M-p <2>: Editing Rebase Sequences. + (line 37) +* M-w: Blaming. (line 134) +* M-w <1>: Common Commands. (line 39) +* MM: Editing Rebase Sequences. + (line 102) +* Mt: Editing Rebase Sequences. + (line 108) +* n: Section Movement. (line 16) +* n <1>: Blaming. (line 118) +* N: Blaming. (line 121) +* n <2>: Editing Rebase Sequences. + (line 34) +* n <3>: Minor Mode for Buffers Visiting Blobs. + (line 16) +* o: Submodule Transient. (line 7) +* O: Subtree. (line 9) +* o a: Submodule Transient. (line 20) +* o d: Submodule Transient. (line 45) +* O e: Subtree. (line 37) +* O e p: Subtree. (line 48) +* O e s: Subtree. (line 52) +* o f: Submodule Transient. (line 51) +* O i: Subtree. (line 13) +* O i a: Subtree. (line 24) +* O i c: Subtree. (line 28) +* O i f: Subtree. (line 34) +* O i m: Subtree. (line 31) +* o l: Submodule Transient. (line 48) +* o p: Submodule Transient. (line 32) +* o r: Submodule Transient. (line 26) +* o s: Submodule Transient. (line 40) +* o u: Submodule Transient. (line 36) +* p: Section Movement. (line 11) +* p <1>: Blaming. (line 124) +* P: Blaming. (line 127) +* p <2>: Editing Rebase Sequences. + (line 31) +* P <1>: Pushing. (line 10) +* p <3>: Minor Mode for Buffers Visiting Blobs. + (line 13) +* P C: Branch Commands. (line 31) +* P e: Pushing. (line 29) +* P m: Pushing. (line 45) +* P o: Pushing. (line 33) +* P p: Pushing. (line 15) +* P r: Pushing. (line 37) +* P t: Pushing. (line 52) +* P T: Pushing. (line 59) +* P u: Pushing. (line 22) +* q: Quitting Windows. (line 7) +* q <1>: Log Buffer. (line 14) +* q <2>: Blaming. (line 130) +* q <3>: Minor Mode for Buffers Visiting Blobs. + (line 19) +* r: Rebasing. (line 10) +* r <1>: Editing Rebase Sequences. + (line 43) +* r a: Rebasing. (line 111) +* r e: Rebasing. (line 42) +* r e <1>: Rebasing. (line 107) +* r f: Rebasing. (line 79) +* r i: Rebasing. (line 76) +* r k: Rebasing. (line 91) +* r m: Rebasing. (line 83) +* r p: Rebasing. (line 28) +* r r: Rebasing. (line 97) +* r s: Rebasing. (line 47) +* r s <1>: Rebasing. (line 103) +* r u: Rebasing. (line 35) +* r w: Rebasing. (line 87) +* RET: Repository List. (line 102) +* RET <1>: References Buffer. (line 159) +* RET <2>: Visiting Files and Blobs from a Diff. + (line 9) +* RET <3>: Blaming. (line 91) +* RET <4>: Editing Rebase Sequences. + (line 15) +* s: Staging and Unstaging. + (line 29) +* S: Staging and Unstaging. + (line 36) +* s <1>: Editing Rebase Sequences. + (line 49) +* S-<tab>: Section Visibility. (line 33) +* SPC: Log Buffer. (line 41) +* SPC <1>: Commands Available in Diffs. + (line 53) +* SPC <2>: Blaming. (line 94) +* SPC <3>: Editing Rebase Sequences. + (line 19) +* t: Editing Rebase Sequences. + (line 97) +* t <1>: Tagging. (line 9) +* T: Notes. (line 9) +* T a: Notes. (line 47) +* T c: Notes. (line 43) +* t k: Tagging. (line 37) +* T m: Notes. (line 35) +* t p: Tagging. (line 43) +* T p: Notes. (line 28) +* t r: Tagging. (line 18) +* T r: Notes. (line 21) +* t t: Tagging. (line 14) +* T T: Notes. (line 14) +* TAB: Section Visibility. (line 10) +* u: Repository List. (line 108) +* u <1>: Staging and Unstaging. + (line 42) +* U: Staging and Unstaging. + (line 50) +* v: Applying. (line 47) +* V: Reverting. (line 7) +* V a: Reverting. (line 35) +* V s: Reverting. (line 32) +* V V: Reverting. (line 15) +* V v: Reverting. (line 20) +* V V <1>: Reverting. (line 29) +* W: Plain Patches. (line 7) +* w: Maildir Patches. (line 9) +* w a: Plain Patches. (line 20) +* w a <1>: Maildir Patches. (line 23) +* w a <2>: Maildir Patches. (line 38) +* W c: Plain Patches. (line 12) +* w m: Maildir Patches. (line 20) +* W s: Plain Patches. (line 26) +* w s: Maildir Patches. (line 34) +* w w: Maildir Patches. (line 14) +* w w <1>: Maildir Patches. (line 31) +* x: Editing Rebase Sequences. + (line 62) +* x <1>: Resetting. (line 9) +* X f: Resetting. (line 44) +* X h: Resetting. (line 24) +* X i: Resetting. (line 33) +* X k: Resetting. (line 28) +* X m: Resetting. (line 15) +* X s: Resetting. (line 19) +* X w: Resetting. (line 39) +* X w <1>: Wip Modes. (line 64) +* Y: Cherries. (line 18) +* y: References Buffer. (line 7) +* y <1>: Editing Rebase Sequences. + (line 74) +* y c: References Buffer. (line 25) +* y o: References Buffer. (line 30) +* y r: References Buffer. (line 34) +* y y: References Buffer. (line 21) +* z: Stashing. (line 9) +* Z: Worktree. (line 9) +* z a: Stashing. (line 52) +* z b: Stashing. (line 105) +* z B: Stashing. (line 110) +* Z b: Worktree. (line 13) +* Z c: Worktree. (line 16) +* z f: Stashing. (line 115) +* Z g: Worktree. (line 26) +* z i: Stashing. (line 20) +* z I: Stashing. (line 42) +* z k: Stashing. (line 98) +* Z k: Worktree. (line 22) +* z l: Stashing. (line 121) +* Z m: Worktree. (line 19) +* z p: Stashing. (line 74) +* z v: Stashing. (line 102) +* z w: Stashing. (line 24) +* z W: Stashing. (line 46) +* z x: Stashing. (line 30) +* z z: Stashing. (line 14) +* z Z: Stashing. (line 36) + + +File: magit.info, Node: Function and Command Index, Next: Variable Index, Prev: Keystroke Index, Up: Top + +Appendix D Function and Command Index +************************************* + + +* Menu: + +* bug-reference-mode: Commit Mode and Hooks. + (line 48) +* forward-line: Editing Rebase Sequences. + (line 34) +* git-commit-ack: Commit Pseudo Headers. + (line 16) +* git-commit-cc: Commit Pseudo Headers. + (line 28) +* git-commit-check-style-conventions: Commit Message Conventions. + (line 33) +* git-commit-insert-pseudo-header: Commit Pseudo Headers. + (line 13) +* git-commit-next-message: Editing Commit Messages. + (line 41) +* git-commit-prev-message: Editing Commit Messages. + (line 36) +* git-commit-propertize-diff: Commit Mode and Hooks. + (line 40) +* git-commit-reported: Commit Pseudo Headers. + (line 31) +* git-commit-review: Commit Pseudo Headers. + (line 19) +* git-commit-save-message: Editing Commit Messages. + (line 33) +* git-commit-save-message <1>: Commit Mode and Hooks. + (line 26) +* git-commit-setup-changelog-support: Commit Mode and Hooks. + (line 29) +* git-commit-signoff: Commit Pseudo Headers. + (line 22) +* git-commit-suggested: Commit Pseudo Headers. + (line 35) +* git-commit-test: Commit Pseudo Headers. + (line 25) +* git-commit-turn-on-auto-fill: Commit Mode and Hooks. + (line 33) +* git-commit-turn-on-flyspell: Commit Mode and Hooks. + (line 36) +* git-rebase-backward-line: Editing Rebase Sequences. + (line 31) +* git-rebase-break: Editing Rebase Sequences. + (line 70) +* git-rebase-edit: Editing Rebase Sequences. + (line 46) +* git-rebase-exec: Editing Rebase Sequences. + (line 62) +* git-rebase-fixup: Editing Rebase Sequences. + (line 52) +* git-rebase-insert: Editing Rebase Sequences. + (line 74) +* git-rebase-kill-line: Editing Rebase Sequences. + (line 56) +* git-rebase-label: Editing Rebase Sequences. + (line 94) +* git-rebase-merge: Editing Rebase Sequences. + (line 102) +* git-rebase-merge-toggle-editmsg: Editing Rebase Sequences. + (line 108) +* git-rebase-move-line-down: Editing Rebase Sequences. + (line 40) +* git-rebase-move-line-up: Editing Rebase Sequences. + (line 37) +* git-rebase-pick: Editing Rebase Sequences. + (line 59) +* git-rebase-reset: Editing Rebase Sequences. + (line 97) +* git-rebase-reword: Editing Rebase Sequences. + (line 43) +* git-rebase-show-commit: Editing Rebase Sequences. + (line 15) +* git-rebase-show-or-scroll-down: Editing Rebase Sequences. + (line 25) +* git-rebase-show-or-scroll-up: Editing Rebase Sequences. + (line 19) +* git-rebase-squash: Editing Rebase Sequences. + (line 49) +* git-rebase-undo: Editing Rebase Sequences. + (line 77) +* ido-enter-magit-status: Status Buffer. (line 96) +* magit-add-section-hook: Section Hooks. (line 20) +* magit-after-save-refresh-status: Automatic Refreshing of Magit Buffers. + (line 55) +* magit-am: Maildir Patches. (line 9) +* magit-am-abort: Maildir Patches. (line 38) +* magit-am-apply-maildir: Maildir Patches. (line 20) +* magit-am-apply-patches: Maildir Patches. (line 14) +* magit-am-continue: Maildir Patches. (line 31) +* magit-am-skip: Maildir Patches. (line 34) +* magit-apply: Applying. (line 34) +* magit-bisect: Bisecting. (line 9) +* magit-bisect-bad: Bisecting. (line 32) +* magit-bisect-good: Bisecting. (line 36) +* magit-bisect-mark: Bisecting. (line 40) +* magit-bisect-reset: Bisecting. (line 51) +* magit-bisect-run: Bisecting. (line 26) +* magit-bisect-skip: Bisecting. (line 46) +* magit-bisect-start: Bisecting. (line 16) +* magit-blame: Blaming. (line 28) +* magit-blame <1>: Blaming. (line 34) +* magit-blame <2>: Blaming. (line 115) +* magit-blame <3>: Commands for Buffers Visiting Files. + (line 52) +* magit-blame <4>: Commands for Buffers Visiting Files. + (line 137) +* magit-blame-addition: Blaming. (line 28) +* magit-blame-addition <1>: Blaming. (line 45) +* magit-blame-additions: Commands for Buffers Visiting Files. + (line 52) +* magit-blame-copy-hash: Blaming. (line 134) +* magit-blame-cycle-style: Blaming. (line 141) +* magit-blame-echo: Blaming. (line 28) +* magit-blame-echo <1>: Blaming. (line 76) +* magit-blame-echo <2>: Commands for Buffers Visiting Files. + (line 52) +* magit-blame-next-chunk: Blaming. (line 118) +* magit-blame-next-chunk-same-commit: Blaming. (line 121) +* magit-blame-previous-chunk: Blaming. (line 124) +* magit-blame-previous-chunk-same-commit: Blaming. (line 127) +* magit-blame-quit: Blaming. (line 28) +* magit-blame-quit <1>: Blaming. (line 87) +* magit-blame-quit <2>: Blaming. (line 130) +* magit-blame-quit <3>: Commands for Buffers Visiting Files. + (line 52) +* magit-blame-removal: Blaming. (line 28) +* magit-blame-removal <1>: Blaming. (line 60) +* magit-blame-removal <2>: Commands for Buffers Visiting Files. + (line 52) +* magit-blame-reverse: Blaming. (line 28) +* magit-blame-reverse <1>: Blaming. (line 68) +* magit-blame-reverse <2>: Commands for Buffers Visiting Files. + (line 52) +* magit-blob-next: Commands for Buffers Visiting Files. + (line 52) +* magit-blob-next <1>: Commands for Buffers Visiting Files. + (line 153) +* magit-blob-next <2>: Minor Mode for Buffers Visiting Blobs. + (line 16) +* magit-blob-previous: Commands for Buffers Visiting Files. + (line 52) +* magit-blob-previous <1>: Commands for Buffers Visiting Files. + (line 149) +* magit-blob-previous <2>: Minor Mode for Buffers Visiting Blobs. + (line 13) +* magit-blob-visit-file: Commands for Buffers Visiting Files. + (line 52) +* magit-blob-visit-file <1>: Commands for Buffers Visiting Files. + (line 160) +* magit-branch: Branch Commands. (line 13) +* magit-branch-and-checkout: Branch Commands. (line 63) +* magit-branch-checkout: Branch Commands. (line 69) +* magit-branch-configure: Branch Commands. (line 31) +* magit-branch-create: Branch Commands. (line 54) +* magit-branch-delete: Branch Commands. (line 138) +* magit-branch-or-checkout: Branch Commands. (line 257) +* magit-branch-orphan: Branch Commands. (line 253) +* magit-branch-rename: Branch Commands. (line 149) +* magit-branch-reset: Branch Commands. (line 123) +* magit-branch-shelve: Auxiliary Branch Commands. + (line 9) +* magit-branch-spinoff: Branch Commands. (line 91) +* magit-branch-spinout: Branch Commands. (line 118) +* magit-branch-unshelve: Auxiliary Branch Commands. + (line 19) +* magit-builtin-completing-read: Support for Completion Frameworks. + (line 41) +* magit-bundle: Bundle. (line 8) +* magit-call-git: Calling Git for Effect. + (line 28) +* magit-call-process: Calling Git for Effect. + (line 31) +* magit-cancel-section: Creating Sections. (line 69) +* magit-checkout: Branch Commands. (line 47) +* magit-cherry: Cherries. (line 18) +* magit-cherry-apply: Cherry Picking. (line 23) +* magit-cherry-copy: Cherry Picking. (line 17) +* magit-cherry-donate: Cherry Picking. (line 51) +* magit-cherry-harvest: Cherry Picking. (line 40) +* magit-cherry-pick: Cherry Picking. (line 9) +* magit-cherry-spinoff: Cherry Picking. (line 72) +* magit-cherry-spinout: Cherry Picking. (line 62) +* magit-clone: Cloning Repository. (line 20) +* magit-clone-bare: Cloning Repository. (line 44) +* magit-clone-mirror: Cloning Repository. (line 48) +* magit-clone-regular: Cloning Repository. (line 28) +* magit-clone-shallow: Cloning Repository. (line 32) +* magit-clone-shallow-exclude: Cloning Repository. (line 61) +* magit-clone-shallow-since: Cloning Repository. (line 55) +* magit-clone-sparse: Cloning Repository. (line 38) +* magit-commit: Initiating a Commit. (line 9) +* magit-commit <1>: Commands for Buffers Visiting Files. + (line 52) +* magit-commit <2>: Commands for Buffers Visiting Files. + (line 176) +* magit-commit-amend: Initiating a Commit. (line 18) +* magit-commit-augment: Initiating a Commit. (line 59) +* magit-commit-create: Initiating a Commit. (line 14) +* magit-commit-extend: Initiating a Commit. (line 21) +* magit-commit-fixup: Initiating a Commit. (line 39) +* magit-commit-instant-fixup: Initiating a Commit. (line 46) +* magit-commit-instant-squash: Initiating a Commit. (line 56) +* magit-commit-reword: Initiating a Commit. (line 30) +* magit-commit-squash: Initiating a Commit. (line 49) +* magit-completing-read: Support for Completion Frameworks. + (line 57) +* magit-copy-buffer-revision: Common Commands. (line 39) +* magit-copy-section-value: Common Commands. (line 22) +* magit-current-section: Section Selection. (line 6) +* magit-cycle-margin-style: Log Margin. (line 63) +* magit-debug-git-executable: Git Executable. (line 55) +* magit-debug-git-executable <1>: Debugging Tools. (line 57) +* magit-define-section-jumper: Creating Sections. (line 74) +* magit-describe-section: Section Types and Values. + (line 14) +* magit-describe-section-briefly: Section Types and Values. + (line 17) +* magit-describe-section-briefly <1>: Matching Sections. (line 7) +* magit-diff: Diffing. (line 22) +* magit-diff <1>: Commands for Buffers Visiting Files. + (line 52) +* magit-diff <2>: Commands for Buffers Visiting Files. + (line 91) +* magit-diff-buffer-file: Commands for Buffers Visiting Files. + (line 52) +* magit-diff-buffer-file <1>: Commands for Buffers Visiting Files. + (line 101) +* magit-diff-default-context: Refreshing Diffs. (line 68) +* magit-diff-dwim: Diffing. (line 27) +* magit-diff-edit-hunk-commit: Commands Available in Diffs. + (line 24) +* magit-diff-flip-revs: Refreshing Diffs. (line 45) +* magit-diff-less-context: Refreshing Diffs. (line 62) +* magit-diff-more-context: Refreshing Diffs. (line 65) +* magit-diff-paths: Diffing. (line 56) +* magit-diff-range: Diffing. (line 30) +* magit-diff-refresh: Refreshing Diffs. (line 16) +* magit-diff-refresh <1>: Refreshing Diffs. (line 21) +* magit-diff-save-default-arguments: Refreshing Diffs. (line 31) +* magit-diff-scope: Matching Sections. (line 110) +* magit-diff-set-default-arguments: Refreshing Diffs. (line 25) +* magit-diff-show-or-scroll-down: Log Buffer. (line 50) +* magit-diff-show-or-scroll-down <1>: Blaming. (line 103) +* magit-diff-show-or-scroll-up: Log Buffer. (line 41) +* magit-diff-show-or-scroll-up <1>: Blaming. (line 94) +* magit-diff-staged: Diffing. (line 48) +* magit-diff-switch-range-type: Refreshing Diffs. (line 41) +* magit-diff-toggle-file-filter: Refreshing Diffs. (line 49) +* magit-diff-toggle-refine-hunk: Refreshing Diffs. (line 38) +* magit-diff-trace-definition: Commands Available in Diffs. + (line 15) +* magit-diff-type: Matching Sections. (line 88) +* magit-diff-unstaged: Diffing. (line 53) +* magit-diff-visit-file: Visiting Files and Blobs from a Diff. + (line 9) +* magit-diff-visit-file-other-frame: Visiting Files and Blobs from a Diff. + (line 71) +* magit-diff-visit-file-other-window: Visiting Files and Blobs from a Diff. + (line 70) +* magit-diff-visit-file-worktree: Visiting Files and Blobs from a Diff. + (line 50) +* magit-diff-visit-worktree-file-other-frame: Visiting Files and Blobs from a Diff. + (line 73) +* magit-diff-visit-worktree-file-other-window: Visiting Files and Blobs from a Diff. + (line 72) +* magit-diff-while-committing: Refreshing Diffs. (line 75) +* magit-diff-while-committing <1>: Editing Commit Messages. + (line 54) +* magit-diff-working-tree: Diffing. (line 43) +* magit-disable-section-inserter: Per-Repository Configuration. + (line 31) +* magit-discard: Applying. (line 40) +* magit-dispatch: Transient Commands. (line 20) +* magit-display-buffer: Switching Buffers. (line 6) +* magit-display-buffer-fullcolumn-most-v1: Switching Buffers. (line 68) +* magit-display-buffer-fullframe-status-topleft-v1: Switching Buffers. + (line 59) +* magit-display-buffer-fullframe-status-v1: Switching Buffers. + (line 54) +* magit-display-buffer-same-window-except-diff-v1: Switching Buffers. + (line 49) +* magit-display-buffer-traditional: Switching Buffers. (line 42) +* magit-display-repository-buffer: Common Commands. (line 9) +* magit-display-repository-buffer <1>: Commands for Buffers Visiting Files. + (line 52) +* magit-display-repository-buffer <2>: Commands for Buffers Visiting Files. + (line 172) +* magit-ediff: Ediffing. (line 21) +* magit-ediff-compare: Ediffing. (line 25) +* magit-ediff-dwim: Ediffing. (line 10) +* magit-ediff-resolve-all: Ediffing. (line 48) +* magit-ediff-resolve-rest: Ediffing. (line 33) +* magit-ediff-show-commit: Ediffing. (line 100) +* magit-ediff-show-staged: Ediffing. (line 94) +* magit-ediff-show-stash: Ediffing. (line 103) +* magit-ediff-show-unstaged: Ediffing. (line 91) +* magit-ediff-show-working-tree: Ediffing. (line 97) +* magit-ediff-stage: Ediffing. (line 87) +* magit-edit-line-commit: Commands for Buffers Visiting Files. + (line 52) +* magit-edit-line-commit <1>: Commands for Buffers Visiting Files. + (line 182) +* magit-emacs-Q-command: Debugging Tools. (line 16) +* magit-fetch: Fetching. (line 10) +* magit-fetch-all: Fetching. (line 45) +* magit-fetch-branch: Fetching. (line 37) +* magit-fetch-from-pushremote: Fetching. (line 15) +* magit-fetch-from-upstream: Fetching. (line 22) +* magit-fetch-modules: Fetching. (line 48) +* magit-fetch-modules <1>: Submodule Transient. (line 51) +* magit-fetch-other: Fetching. (line 34) +* magit-fetch-refspec: Fetching. (line 41) +* magit-file-checkout: Resetting. (line 44) +* magit-file-checkout <1>: Commands for Buffers Visiting Files. + (line 52) +* magit-file-checkout <2>: Commands for Buffers Visiting Files. + (line 86) +* magit-file-delete: Commands for Buffers Visiting Files. + (line 52) +* magit-file-delete <1>: Commands for Buffers Visiting Files. + (line 82) +* magit-file-dispatch: Commands for Buffers Visiting Files. + (line 52) +* magit-file-dispatch <1>: Commands for Buffers Visiting Files. + (line 58) +* magit-file-rename: Commands for Buffers Visiting Files. + (line 52) +* magit-file-rename <1>: Commands for Buffers Visiting Files. + (line 78) +* magit-file-untrack: Commands for Buffers Visiting Files. + (line 52) +* magit-file-untrack <1>: Commands for Buffers Visiting Files. + (line 74) +* magit-find-file: General-Purpose Visit Commands. + (line 9) +* magit-find-file <1>: Commands for Buffers Visiting Files. + (line 52) +* magit-find-file <2>: Commands for Buffers Visiting Files. + (line 156) +* magit-find-file-other-frame: General-Purpose Visit Commands. + (line 19) +* magit-find-file-other-window: General-Purpose Visit Commands. + (line 14) +* magit-generate-buffer-name-default-function: Naming Buffers. + (line 16) +* magit-get-section: Matching Sections. (line 14) +* magit-git: Calling Git for Effect. + (line 46) +* magit-git-command: Running Git Manually. + (line 25) +* magit-git-command-topdir: Running Git Manually. + (line 17) +* magit-git-exit-code: Getting a Value from Git. + (line 10) +* magit-git-failure: Getting a Value from Git. + (line 17) +* magit-git-false: Getting a Value from Git. + (line 25) +* magit-git-insert: Getting a Value from Git. + (line 29) +* magit-git-items: Getting a Value from Git. + (line 41) +* magit-git-lines: Getting a Value from Git. + (line 37) +* magit-git-mergetool: Running Git Manually. + (line 62) +* magit-git-mergetool <1>: Ediffing. (line 79) +* magit-git-str: Getting a Value from Git. + (line 75) +* magit-git-string: Getting a Value from Git. + (line 32) +* magit-git-success: Getting a Value from Git. + (line 13) +* magit-git-true: Getting a Value from Git. + (line 21) +* magit-git-wash: Calling Git for Effect. + (line 50) +* magit-go-backward: Log Buffer. (line 20) +* magit-go-backward <1>: Refreshing Diffs. (line 84) +* magit-go-forward: Log Buffer. (line 23) +* magit-go-forward <1>: Refreshing Diffs. (line 87) +* magit-hunk-set-window-start: Section Movement. (line 45) +* magit-ido-completing-read: Support for Completion Frameworks. + (line 46) +* magit-init: Creating Repository. (line 7) +* magit-insert-am-sequence: Status Sections. (line 25) +* magit-insert-assumed-unchanged-files: Status Sections. (line 98) +* magit-insert-bisect-log: Status Sections. (line 39) +* magit-insert-bisect-output: Status Sections. (line 33) +* magit-insert-bisect-rest: Status Sections. (line 36) +* magit-insert-diff-filter-header: Status Header Sections. + (line 35) +* magit-insert-error-header: Status Header Sections. + (line 26) +* magit-insert-head-branch-header: Status Header Sections. + (line 38) +* magit-insert-heading: Creating Sections. (line 41) +* magit-insert-ignored-files: Status Sections. (line 83) +* magit-insert-local-branches: References Sections. (line 16) +* magit-insert-merge-log: Status Sections. (line 17) +* magit-insert-modules: Status Module Sections. + (line 12) +* magit-insert-modules-overview: Status Module Sections. + (line 30) +* magit-insert-modules-unpulled-from-pushremote: Status Module Sections. + (line 45) +* magit-insert-modules-unpulled-from-upstream: Status Module Sections. + (line 40) +* magit-insert-modules-unpushed-to-pushremote: Status Module Sections. + (line 55) +* magit-insert-modules-unpushed-to-upstream: Status Module Sections. + (line 50) +* magit-insert-push-branch-header: Status Header Sections. + (line 45) +* magit-insert-rebase-sequence: Status Sections. (line 21) +* magit-insert-recent-commits: Status Sections. (line 110) +* magit-insert-remote-branches: References Sections. (line 19) +* magit-insert-remote-header: Status Header Sections. + (line 58) +* magit-insert-repo-header: Status Header Sections. + (line 55) +* magit-insert-section: Creating Sections. (line 6) +* magit-insert-sequencer-sequence: Status Sections. (line 29) +* magit-insert-skip-worktree-files: Status Sections. (line 92) +* magit-insert-staged-changes: Status Sections. (line 53) +* magit-insert-stashes: Status Sections. (line 56) +* magit-insert-status-headers: Status Header Sections. + (line 12) +* magit-insert-tags: References Sections. (line 22) +* magit-insert-tags-header: Status Header Sections. + (line 49) +* magit-insert-tracked-files: Status Sections. (line 80) +* magit-insert-unpulled-cherries: Status Sections. (line 119) +* magit-insert-unpulled-from-pushremote: Status Sections. (line 66) +* magit-insert-unpulled-from-upstream: Status Sections. (line 62) +* magit-insert-unpulled-or-recent-commits: Status Sections. (line 104) +* magit-insert-unpushed-cherries: Status Sections. (line 125) +* magit-insert-unpushed-to-pushremote: Status Sections. (line 74) +* magit-insert-unpushed-to-upstream: Status Sections. (line 70) +* magit-insert-unstaged-changes: Status Sections. (line 50) +* magit-insert-untracked-files: Status Sections. (line 42) +* magit-insert-upstream-branch-header: Status Header Sections. + (line 41) +* magit-insert-user-header: Status Header Sections. + (line 65) +* magit-jump-to-diffstat-or-diff: Commands Available in Diffs. + (line 43) +* magit-kill-this-buffer: Minor Mode for Buffers Visiting Blobs. + (line 19) +* magit-list-repositories: Repository List. (line 6) +* magit-list-submodules: Listing Submodules. (line 13) +* magit-list-submodules <1>: Submodule Transient. (line 48) +* magit-log: Logging. (line 28) +* magit-log <1>: Commands for Buffers Visiting Files. + (line 52) +* magit-log <2>: Commands for Buffers Visiting Files. + (line 109) +* magit-log-all: Logging. (line 59) +* magit-log-all-branches: Logging. (line 56) +* magit-log-branches: Logging. (line 53) +* magit-log-buffer-file: Commands for Buffers Visiting Files. + (line 52) +* magit-log-buffer-file <1>: Commands for Buffers Visiting Files. + (line 119) +* magit-log-bury-buffer: Log Buffer. (line 14) +* magit-log-current: Logging. (line 33) +* magit-log-double-commit-limit: Log Buffer. (line 64) +* magit-log-half-commit-limit: Log Buffer. (line 67) +* magit-log-head: Logging. (line 38) +* magit-log-maybe-show-more-commits: Section Movement. (line 58) +* magit-log-maybe-update-blob-buffer: Section Movement. (line 72) +* magit-log-maybe-update-revision-buffer: Section Movement. (line 65) +* magit-log-merged: Commands for Buffers Visiting Files. + (line 52) +* magit-log-merged <1>: Commands for Buffers Visiting Files. + (line 132) +* magit-log-move-to-parent: Log Buffer. (line 26) +* magit-log-move-to-revision: Log Buffer. (line 31) +* magit-log-other: Logging. (line 47) +* magit-log-refresh: Refreshing Logs. (line 12) +* magit-log-refresh <1>: Refreshing Logs. (line 17) +* magit-log-refresh <2>: Log Buffer. (line 7) +* magit-log-related: Logging. (line 41) +* magit-log-save-default-arguments: Refreshing Logs. (line 27) +* magit-log-select-pick: Select from Log. (line 21) +* magit-log-select-quit: Select from Log. (line 26) +* magit-log-set-default-arguments: Refreshing Logs. (line 21) +* magit-log-toggle-commit-limit: Log Buffer. (line 59) +* magit-log-trace-definition: Commands for Buffers Visiting Files. + (line 52) +* magit-log-trace-definition <1>: Commands for Buffers Visiting Files. + (line 129) +* magit-margin-settings: Log Margin. (line 52) +* magit-maybe-set-dedicated: Switching Buffers. (line 89) +* magit-merge: Merging. (line 10) +* magit-merge <1>: Merging. (line 86) +* magit-merge-abort: Merging. (line 91) +* magit-merge-absorb: Merging. (line 42) +* magit-merge-editmsg: Merging. (line 30) +* magit-merge-into: Merging. (line 54) +* magit-merge-nocommit: Merging. (line 36) +* magit-merge-plain: Merging. (line 18) +* magit-merge-preview: Merging. (line 75) +* magit-merge-squash: Merging. (line 67) +* magit-mode-bury-buffer: Quitting Windows. (line 7) +* magit-mode-display-buffer: Refreshing Buffers. (line 32) +* magit-mode-quit-window: Quitting Windows. (line 34) +* magit-mode-setup: Refreshing Buffers. (line 17) +* magit-notes: Notes. (line 9) +* magit-notes-edit: Notes. (line 14) +* magit-notes-merge: Notes. (line 35) +* magit-notes-merge-abort: Notes. (line 47) +* magit-notes-merge-commit: Notes. (line 43) +* magit-notes-prune: Notes. (line 28) +* magit-notes-remove: Notes. (line 21) +* magit-patch: Plain Patches. (line 7) +* magit-patch-apply: Plain Patches. (line 20) +* magit-patch-apply <1>: Maildir Patches. (line 23) +* magit-patch-create: Plain Patches. (line 12) +* magit-patch-save: Plain Patches. (line 26) +* magit-pop-revision-stack: Using the Revision Stack. + (line 7) +* magit-process: Viewing Git Output. (line 17) +* magit-process-file: Getting a Value from Git. + (line 57) +* magit-process-git: Getting a Value from Git. + (line 50) +* magit-process-kill: Viewing Git Output. (line 24) +* magit-pull: Pulling. (line 10) +* magit-pull-branch: Pulling. (line 28) +* magit-pull-from-pushremote: Pulling. (line 14) +* magit-pull-from-upstream: Pulling. (line 21) +* magit-push: Pushing. (line 10) +* magit-push-current: Pushing. (line 29) +* magit-push-current-to-pushremote: Pushing. (line 15) +* magit-push-current-to-upstream: Pushing. (line 22) +* magit-push-implicitly: Pushing. (line 74) +* magit-push-matching: Pushing. (line 45) +* magit-push-other: Pushing. (line 33) +* magit-push-refspecs: Pushing. (line 37) +* magit-push-tag: Pushing. (line 59) +* magit-push-tags: Pushing. (line 52) +* magit-push-to-remote: Pushing. (line 91) +* magit-rebase: Rebasing. (line 10) +* magit-rebase-abort: Rebasing. (line 111) +* magit-rebase-autosquash: Rebasing. (line 79) +* magit-rebase-branch: Rebasing. (line 42) +* magit-rebase-continue: Rebasing. (line 97) +* magit-rebase-edit: Rebasing. (line 107) +* magit-rebase-edit-commit: Rebasing. (line 83) +* magit-rebase-interactive: Rebasing. (line 76) +* magit-rebase-onto-pushremote: Rebasing. (line 28) +* magit-rebase-onto-upstream: Rebasing. (line 35) +* magit-rebase-remove-commit: Rebasing. (line 91) +* magit-rebase-reword-commit: Rebasing. (line 87) +* magit-rebase-skip: Rebasing. (line 103) +* magit-rebase-subset: Rebasing. (line 47) +* magit-reflog-current: Reflog. (line 12) +* magit-reflog-head: Reflog. (line 18) +* magit-reflog-other: Reflog. (line 15) +* magit-refresh: Automatic Refreshing of Magit Buffers. + (line 26) +* magit-refresh-all: Automatic Refreshing of Magit Buffers. + (line 34) +* magit-refs-set-show-commit-count: References Buffer. (line 34) +* magit-region-sections: Section Selection. (line 9) +* magit-region-values: Section Selection. (line 35) +* magit-remote: Remote Commands. (line 14) +* magit-remote-add: Remote Commands. (line 48) +* magit-remote-configure: Remote Commands. (line 32) +* magit-remote-prune: Remote Commands. (line 63) +* magit-remote-prune-refspecs: Remote Commands. (line 67) +* magit-remote-remove: Remote Commands. (line 60) +* magit-remote-rename: Remote Commands. (line 52) +* magit-remote-set-url: Remote Commands. (line 56) +* magit-repolist-column-branch: Repository List. (line 51) +* magit-repolist-column-branches: Repository List. (line 58) +* magit-repolist-column-flag: Repository List. (line 64) +* magit-repolist-column-flags: Repository List. (line 76) +* magit-repolist-column-ident: Repository List. (line 40) +* magit-repolist-column-path: Repository List. (line 44) +* magit-repolist-column-stashes: Repository List. (line 61) +* magit-repolist-column-unpulled-from-pushremote: Repository List. + (line 87) +* magit-repolist-column-unpulled-from-upstream: Repository List. + (line 83) +* magit-repolist-column-unpushed-to-pushremote: Repository List. + (line 95) +* magit-repolist-column-unpushed-to-upstream: Repository List. + (line 91) +* magit-repolist-column-upstream: Repository List. (line 54) +* magit-repolist-column-version: Repository List. (line 47) +* magit-repolist-fetch: Repository List. (line 111) +* magit-repolist-find-file-other-frame: Repository List. (line 115) +* magit-repolist-mark: Repository List. (line 105) +* magit-repolist-status: Repository List. (line 102) +* magit-repolist-unmark: Repository List. (line 108) +* magit-reset-hard: Resetting. (line 24) +* magit-reset-index: Staging and Unstaging. + (line 78) +* magit-reset-index <1>: Resetting. (line 33) +* magit-reset-keep: Resetting. (line 28) +* magit-reset-mixed: Resetting. (line 15) +* magit-reset-quickly: Resetting. (line 9) +* magit-reset-soft: Resetting. (line 19) +* magit-reset-worktree: Resetting. (line 39) +* magit-reset-worktree <1>: Wip Modes. (line 64) +* magit-restore-window-configuration: Quitting Windows. (line 24) +* magit-reverse: Applying. (line 47) +* magit-reverse-in-index: Staging and Unstaging. + (line 58) +* magit-revert: Reverting. (line 7) +* magit-revert-and-commit: Reverting. (line 15) +* magit-revert-no-commit: Reverting. (line 20) +* magit-run: Running Git Manually. + (line 13) +* magit-run-git: Calling Git for Effect. + (line 34) +* magit-run-git-async: Calling Git for Effect. + (line 59) +* magit-run-git-gui: Running Git Manually. + (line 59) +* magit-run-git-with-editor: Calling Git for Effect. + (line 71) +* magit-run-git-with-input: Calling Git for Effect. + (line 37) +* magit-run-gitk: Running Git Manually. + (line 50) +* magit-run-gitk-all: Running Git Manually. + (line 53) +* magit-run-gitk-branches: Running Git Manually. + (line 56) +* magit-save-window-configuration: Switching Buffers. (line 80) +* magit-section-backward: Section Movement. (line 11) +* magit-section-backward-siblings: Section Movement. (line 19) +* magit-section-case: Matching Sections. (line 66) +* magit-section-cycle: Section Visibility. (line 14) +* magit-section-cycle-diffs: Section Visibility. (line 29) +* magit-section-cycle-global: Section Visibility. (line 33) +* magit-section-forward: Section Movement. (line 16) +* magit-section-forward-siblings: Section Movement. (line 24) +* magit-section-hide: Section Visibility. (line 55) +* magit-section-hide-children: Section Visibility. (line 67) +* magit-section-ident: Matching Sections. (line 10) +* magit-section-match: Matching Sections. (line 18) +* magit-section-set-window-start: Section Movement. (line 52) +* magit-section-show: Section Visibility. (line 52) +* magit-section-show-children: Section Visibility. (line 62) +* magit-section-show-headings: Section Visibility. (line 58) +* magit-section-show-level-1: Section Visibility. (line 39) +* magit-section-show-level-1-all: Section Visibility. (line 45) +* magit-section-show-level-2: Section Visibility. (line 39) +* magit-section-show-level-2-all: Section Visibility. (line 45) +* magit-section-show-level-3: Section Visibility. (line 39) +* magit-section-show-level-3-all: Section Visibility. (line 45) +* magit-section-show-level-4: Section Visibility. (line 39) +* magit-section-show-level-4-all: Section Visibility. (line 45) +* magit-section-toggle: Section Visibility. (line 10) +* magit-section-toggle-children: Section Visibility. (line 70) +* magit-section-up: Section Movement. (line 28) +* magit-section-value-if: Matching Sections. (line 57) +* magit-sequence-abort: Cherry Picking. (line 91) +* magit-sequence-abort <1>: Reverting. (line 35) +* magit-sequence-continue: Cherry Picking. (line 85) +* magit-sequence-continue <1>: Reverting. (line 29) +* magit-sequence-skip: Cherry Picking. (line 88) +* magit-sequence-skip <1>: Reverting. (line 32) +* magit-shell-command: Running Git Manually. + (line 38) +* magit-shell-command-topdir: Running Git Manually. + (line 34) +* magit-show-commit: Diffing. (line 63) +* magit-show-commit <1>: Blaming. (line 91) +* magit-show-refs: References Buffer. (line 7) +* magit-show-refs-current: References Buffer. (line 25) +* magit-show-refs-head: References Buffer. (line 21) +* magit-show-refs-other: References Buffer. (line 30) +* magit-snapshot-both: Stashing. (line 36) +* magit-snapshot-index: Stashing. (line 42) +* magit-snapshot-worktree: Stashing. (line 46) +* magit-sparse-checkout: Sparse checkouts. (line 17) +* magit-sparse-checkout-add: Sparse checkouts. (line 39) +* magit-sparse-checkout-disable: Sparse checkouts. (line 50) +* magit-sparse-checkout-enable: Sparse checkouts. (line 21) +* magit-sparse-checkout-reapply: Sparse checkouts. (line 44) +* magit-sparse-checkout-set: Sparse checkouts. (line 33) +* magit-stage: Staging and Unstaging. + (line 29) +* magit-stage-buffer-file: Commands for Buffers Visiting Files. + (line 52) +* magit-stage-buffer-file <1>: Commands for Buffers Visiting Files. + (line 63) +* magit-stage-file: Staging from File-Visiting Buffers. + (line 11) +* magit-stage-file <1>: Commands for Buffers Visiting Files. + (line 52) +* magit-stage-file <2>: Commands for Buffers Visiting Files. + (line 63) +* magit-stage-modified: Staging and Unstaging. + (line 36) +* magit-start-git: Calling Git for Effect. + (line 82) +* magit-start-process: Calling Git for Effect. + (line 100) +* magit-stash: Stashing. (line 9) +* magit-stash-apply: Stashing. (line 52) +* magit-stash-both: Stashing. (line 14) +* magit-stash-branch: Stashing. (line 105) +* magit-stash-branch-here: Stashing. (line 110) +* magit-stash-clear: Stashing. (line 118) +* magit-stash-drop: Stashing. (line 98) +* magit-stash-format-patch: Stashing. (line 115) +* magit-stash-index: Stashing. (line 20) +* magit-stash-keep-index: Stashing. (line 30) +* magit-stash-list: Stashing. (line 121) +* magit-stash-pop: Stashing. (line 74) +* magit-stash-show: Diffing. (line 67) +* magit-stash-show <1>: Stashing. (line 102) +* magit-stash-worktree: Stashing. (line 24) +* magit-stashes-maybe-update-stash-buffer: Section Movement. (line 92) +* magit-status: Status Buffer. (line 23) +* magit-status-here: Commands for Buffers Visiting Files. + (line 52) +* magit-status-here <1>: Commands for Buffers Visiting Files. + (line 166) +* magit-status-maybe-update-blob-buffer: Section Movement. (line 87) +* magit-status-maybe-update-revision-buffer: Section Movement. + (line 77) +* magit-status-maybe-update-stash-buffer: Section Movement. (line 82) +* magit-status-quick: Status Buffer. (line 70) +* magit-submodule: Submodule Transient. (line 7) +* magit-submodule-add: Submodule Transient. (line 20) +* magit-submodule-populate: Submodule Transient. (line 32) +* magit-submodule-register: Submodule Transient. (line 26) +* magit-submodule-synchronize: Submodule Transient. (line 40) +* magit-submodule-unpopulate: Submodule Transient. (line 45) +* magit-submodule-update: Submodule Transient. (line 36) +* magit-subtree: Subtree. (line 9) +* magit-subtree-add: Subtree. (line 24) +* magit-subtree-add-commit: Subtree. (line 28) +* magit-subtree-export: Subtree. (line 37) +* magit-subtree-import: Subtree. (line 13) +* magit-subtree-merge: Subtree. (line 31) +* magit-subtree-pull: Subtree. (line 34) +* magit-subtree-push: Subtree. (line 48) +* magit-subtree-split: Subtree. (line 52) +* magit-switch-to-repository-buffer: Common Commands. (line 6) +* magit-switch-to-repository-buffer-other-frame: Common Commands. + (line 8) +* magit-switch-to-repository-buffer-other-window: Common Commands. + (line 7) +* magit-tag: Tagging. (line 9) +* magit-tag-create: Tagging. (line 14) +* magit-tag-delete: Tagging. (line 37) +* magit-tag-prune: Tagging. (line 43) +* magit-tag-release: Tagging. (line 18) +* magit-toggle-buffer-lock: Modes and Buffers. (line 18) +* magit-toggle-git-debug: Debugging Tools. (line 29) +* magit-toggle-margin: Refreshing Logs. (line 34) +* magit-toggle-margin <1>: Log Margin. (line 60) +* magit-toggle-margin-details: Log Margin. (line 66) +* magit-toggle-verbose-refresh: Debugging Tools. (line 52) +* magit-unstage: Staging and Unstaging. + (line 42) +* magit-unstage-all: Staging and Unstaging. + (line 50) +* magit-unstage-buffer-file: Commands for Buffers Visiting Files. + (line 52) +* magit-unstage-buffer-file <1>: Commands for Buffers Visiting Files. + (line 69) +* magit-unstage-file: Staging from File-Visiting Buffers. + (line 18) +* magit-unstage-file <1>: Commands for Buffers Visiting Files. + (line 52) +* magit-unstage-file <2>: Commands for Buffers Visiting Files. + (line 69) +* magit-version: Git Executable. (line 59) +* magit-version <1>: Debugging Tools. (line 11) +* magit-visit-ref: References Buffer. (line 159) +* magit-wip-commit: Wip Modes. (line 85) +* magit-wip-log: Wip Modes. (line 47) +* magit-wip-log-current: Wip Modes. (line 55) +* magit-worktree: Worktree. (line 9) +* magit-worktree-branch: Worktree. (line 16) +* magit-worktree-checkout: Worktree. (line 13) +* magit-worktree-delete: Worktree. (line 22) +* magit-worktree-move: Worktree. (line 19) +* magit-worktree-status: Worktree. (line 26) +* scroll-down: Commands Available in Diffs. + (line 56) +* scroll-up: Commands Available in Diffs. + (line 53) +* with-editor-cancel: Editing Commit Messages. + (line 22) +* with-editor-cancel <1>: Editing Rebase Sequences. + (line 11) +* with-editor-debug: Debugging Tools. (line 64) +* with-editor-finish: Editing Commit Messages. + (line 18) +* with-editor-finish <1>: Editing Rebase Sequences. + (line 7) +* with-editor-usage-message: Commit Mode and Hooks. + (line 51) + + +File: magit.info, Node: Variable Index, Prev: Function and Command Index, Up: Top + +Appendix E Variable Index +************************* + + +* Menu: + +* auto-revert-buffer-list-filter: Automatic Reverting of File-Visiting Buffers. + (line 73) +* auto-revert-interval: Automatic Reverting of File-Visiting Buffers. + (line 69) +* auto-revert-mode: Automatic Reverting of File-Visiting Buffers. + (line 57) +* auto-revert-stop-on-user-input: Automatic Reverting of File-Visiting Buffers. + (line 65) +* auto-revert-use-notify: Automatic Reverting of File-Visiting Buffers. + (line 46) +* auto-revert-verbose: Automatic Reverting of File-Visiting Buffers. + (line 94) +* branch.autoSetupMerge: Branch Git Variables. + (line 71) +* branch.autoSetupRebase: Branch Git Variables. + (line 85) +* branch.NAME.description: Branch Git Variables. + (line 42) +* branch.NAME.merge: Branch Git Variables. + (line 10) +* branch.NAME.pushRemote: Branch Git Variables. + (line 29) +* branch.NAME.rebase: Branch Git Variables. + (line 20) +* branch.NAME.remote: Branch Git Variables. + (line 15) +* core.notesRef: Notes. (line 53) +* git-commit-finish-query-functions: Commit Message Conventions. + (line 18) +* git-commit-known-pseudo-headers: Commit Pseudo Headers. + (line 9) +* git-commit-major-mode: Commit Mode and Hooks. + (line 12) +* git-commit-post-finish-hook: Commit Mode and Hooks. + (line 54) +* git-commit-setup-hook: Commit Mode and Hooks. + (line 21) +* git-commit-style-convention-checks: Commit Message Conventions. + (line 38) +* git-commit-summary-max-length: Commit Message Conventions. + (line 13) +* git-rebase-auto-advance: Editing Rebase Sequences. + (line 80) +* git-rebase-confirm-cancel: Editing Rebase Sequences. + (line 86) +* git-rebase-show-instructions: Editing Rebase Sequences. + (line 83) +* global-auto-revert-mode: Automatic Reverting of File-Visiting Buffers. + (line 21) +* magit-auto-revert-immediately: Automatic Reverting of File-Visiting Buffers. + (line 30) +* magit-auto-revert-mode: Automatic Reverting of File-Visiting Buffers. + (line 17) +* magit-auto-revert-tracked-only: Automatic Reverting of File-Visiting Buffers. + (line 51) +* magit-bisect-show-graph: Bisecting. (line 57) +* magit-blame-disable-modes: Blaming. (line 165) +* magit-blame-echo-style: Blaming. (line 151) +* magit-blame-goto-chunk-hook: Blaming. (line 170) +* magit-blame-read-only: Blaming. (line 161) +* magit-blame-styles: Blaming. (line 147) +* magit-blame-time-format: Blaming. (line 157) +* magit-branch-adjust-remote-upstream-alist: Branch Commands. (line 202) +* magit-branch-direct-configure: Branch Commands. (line 19) +* magit-branch-prefer-remote-upstream: Branch Commands. (line 158) +* magit-branch-read-upstream-first: Branch Commands. (line 153) +* magit-buffer-name-format: Naming Buffers. (line 25) +* magit-bury-buffer-function: Quitting Windows. (line 16) +* magit-cherry-margin: Cherries. (line 21) +* magit-clone-always-transient: Cloning Repository. (line 12) +* magit-clone-default-directory: Cloning Repository. (line 84) +* magit-clone-name-alist: Cloning Repository. (line 94) +* magit-clone-set-remote-head: Cloning Repository. (line 66) +* magit-clone-set-remote.pushDefault: Cloning Repository. (line 75) +* magit-clone-url-format: Cloning Repository. (line 114) +* magit-commit-ask-to-stage: Initiating a Commit. (line 65) +* magit-commit-diff-inhibit-same-window: Initiating a Commit. (line 97) +* magit-commit-extend-override-date: Initiating a Commit. (line 72) +* magit-commit-reword-override-date: Initiating a Commit. (line 75) +* magit-commit-show-diff: Initiating a Commit. (line 69) +* magit-commit-squash-confirm: Initiating a Commit. (line 78) +* magit-completing-read-function: Support for Completion Frameworks. + (line 27) +* magit-define-global-key-bindings: Global Bindings. (line 6) +* magit-diff-adjust-tab-width: Diff Options. (line 17) +* magit-diff-buffer-file-locked: Commands for Buffers Visiting Files. + (line 104) +* magit-diff-extra-stat-arguments: Diff Options. (line 112) +* magit-diff-hide-trailing-cr-characters: Diff Options. (line 77) +* magit-diff-highlight-hunk-region-functions: Diff Options. (line 80) +* magit-diff-highlight-indentation: Diff Options. (line 63) +* magit-diff-highlight-trailing: Diff Options. (line 59) +* magit-diff-paint-whitespace: Diff Options. (line 38) +* magit-diff-paint-whitespace-lines: Diff Options. (line 52) +* magit-diff-refine-hunk: Diff Options. (line 6) +* magit-diff-refine-ignore-whitespace: Diff Options. (line 13) +* magit-diff-unmarked-lines-keep-foreground: Diff Options. (line 105) +* magit-diff-visit-previous-blob: Visiting Files and Blobs from a Diff. + (line 38) +* magit-direct-use-buffer-arguments: Transient Arguments and Buffer Variables. + (line 73) +* magit-display-buffer-function: Switching Buffers. (line 25) +* magit-display-buffer-noselect: Switching Buffers. (line 17) +* magit-dwim-selection: Completion and Confirmation. + (line 42) +* magit-ediff-dwim-resolve-function: Ediffing. (line 105) +* magit-ediff-dwim-show-on-hunks: Ediffing. (line 111) +* magit-ediff-quit-hook: Ediffing. (line 124) +* magit-ediff-show-stash-with-index: Ediffing. (line 118) +* magit-generate-buffer-name-function: Naming Buffers. (line 6) +* magit-git-debug: Viewing Git Output. (line 26) +* magit-git-debug <1>: Getting a Value from Git. + (line 68) +* magit-git-executable: Git Executable. (line 26) +* magit-git-global-arguments: Global Git Arguments. + (line 6) +* magit-keep-region-overlay: The Selection. (line 52) +* magit-list-refs-sortby: Additional Completion Options. + (line 6) +* magit-log-auto-more: Log Buffer. (line 69) +* magit-log-buffer-file-locked: Commands for Buffers Visiting Files. + (line 124) +* magit-log-margin: Log Margin. (line 12) +* magit-log-margin-show-committer-date: Log Margin. (line 44) +* magit-log-section-commit-count: Status Sections. (line 114) +* magit-log-select-margin: Select from Log. (line 28) +* magit-log-show-color-graph-limit: Log Buffer. (line 78) +* magit-log-show-refname-after-summary: Log Buffer. (line 74) +* magit-log-show-signatures-limit: Log Buffer. (line 84) +* magit-log-trace-definition-function: Commands Available in Diffs. + (line 17) +* magit-module-sections-hook: Status Module Sections. + (line 19) +* magit-module-sections-nested: Status Module Sections. + (line 22) +* magit-no-confirm: Action Confirmation. (line 18) +* magit-pop-revision-stack-format: Using the Revision Stack. + (line 34) +* magit-post-clone-hook: Cloning Repository. (line 133) +* magit-post-commit-hook: Initiating a Commit. (line 86) +* magit-post-display-buffer-hook: Switching Buffers. (line 85) +* magit-pre-display-buffer-hook: Switching Buffers. (line 76) +* magit-prefer-remote-upstream: Branch Git Variables. + (line 108) +* magit-prefix-use-buffer-arguments: Transient Arguments and Buffer Variables. + (line 65) +* magit-process-extreme-logging: Viewing Git Output. (line 56) +* magit-process-raise-error: Calling Git for Effect. + (line 125) +* magit-pull-or-fetch: Fetching. (line 52) +* magit-reflog-margin: Reflog. (line 20) +* magit-refresh-args: Refreshing Buffers. (line 52) +* magit-refresh-buffer-hook: Automatic Refreshing of Magit Buffers. + (line 41) +* magit-refresh-function: Refreshing Buffers. (line 47) +* magit-refresh-status-buffer: Automatic Refreshing of Magit Buffers. + (line 46) +* magit-refs-filter-alist: References Buffer. (line 137) +* magit-refs-focus-column-width: References Buffer. (line 75) +* magit-refs-margin: References Buffer. (line 89) +* magit-refs-margin-for-tags: References Buffer. (line 112) +* magit-refs-pad-commit-counts: References Buffer. (line 45) +* magit-refs-primary-column-width: References Buffer. (line 63) +* magit-refs-sections-hook: References Sections. (line 13) +* magit-refs-show-commit-count: References Buffer. (line 36) +* magit-refs-show-remote-prefix: References Buffer. (line 57) +* magit-remote-add-set-remote.pushDefault: Remote Commands. (line 83) +* magit-remote-direct-configure: Remote Commands. (line 20) +* magit-remote-git-executable: Git Executable. (line 32) +* magit-repolist-columns: Repository List. (line 12) +* magit-repository-directories: Status Buffer. (line 57) +* magit-revision-filter-files-on-follow: Revision Buffer. (line 55) +* magit-revision-insert-related-refs: Revision Buffer. (line 6) +* magit-revision-show-gravatars: Revision Buffer. (line 15) +* magit-revision-use-hash-sections: Revision Buffer. (line 31) +* magit-root-section: Matching Sections. (line 81) +* magit-save-repository-buffers: Automatic Saving of File-Visiting Buffers. + (line 13) +* magit-section-cache-visibility: Section Visibility. (line 95) +* magit-section-initial-visibility-alist: Section Visibility. (line 79) +* magit-section-movement-hook: Section Movement. (line 41) +* magit-section-set-visibility-hook: Section Visibility. (line 105) +* magit-section-show-child-count: Section Options. (line 9) +* magit-section-visibility-indicator: Section Visibility. (line 122) +* magit-shell-command-verbose-prompt: Running Git Manually. + (line 43) +* magit-stashes-margin: Stashing. (line 123) +* magit-status-headers-hook: Status Header Sections. + (line 17) +* magit-status-margin: Status Options. (line 6) +* magit-status-sections-hook: Status Sections. (line 10) +* magit-submodule-list-columns: Listing Submodules. (line 20) +* magit-this-process: Calling Git for Effect. + (line 121) +* magit-uniquify-buffer-names: Naming Buffers. (line 65) +* magit-unstage-committed: Staging and Unstaging. + (line 52) +* magit-update-other-window-delay: Section Movement. (line 97) +* magit-visit-ref-behavior: References Buffer. (line 168) +* magit-wip-after-apply-mode: Legacy Wip Modes. (line 18) +* magit-wip-after-apply-mode-lighter: Legacy Wip Modes. (line 54) +* magit-wip-after-save-local-mode-lighter: Legacy Wip Modes. (line 51) +* magit-wip-after-save-mode: Legacy Wip Modes. (line 13) +* magit-wip-before-change-mode: Legacy Wip Modes. (line 31) +* magit-wip-before-change-mode-lighter: Legacy Wip Modes. (line 57) +* magit-wip-initial-backup-mode: Legacy Wip Modes. (line 35) +* magit-wip-initial-backup-mode-lighter: Legacy Wip Modes. (line 60) +* magit-wip-merge-branch: Wip Graph. (line 6) +* magit-wip-mode: Wip Modes. (line 30) +* magit-wip-mode-lighter: Wip Modes. (line 98) +* magit-wip-namespace: Wip Modes. (line 91) +* notes.displayRef: Notes. (line 57) +* pull.rebase: Branch Git Variables. + (line 50) +* remote.NAME.fetch: Remote Git Variables. + (line 14) +* remote.NAME.push: Remote Git Variables. + (line 23) +* remote.NAME.pushurl: Remote Git Variables. + (line 18) +* remote.NAME.tagOpts: Remote Git Variables. + (line 27) +* remote.NAME.url: Remote Git Variables. + (line 10) +* remote.pushDefault: Branch Git Variables. + (line 62) + + + +Tag Table: +Node: Top774 +Node: Introduction6566 +Node: Installation11282 +Node: Installing from Melpa11612 +Node: Installing from the Git Repository12687 +Node: Post-Installation Tasks15501 +Node: Getting Started16786 +Node: Interface Concepts22597 +Node: Modes and Buffers22976 +Node: Switching Buffers24687 +Node: Naming Buffers29426 +Node: Quitting Windows32501 +Node: Automatic Refreshing of Magit Buffers34436 +Node: Automatic Saving of File-Visiting Buffers37317 +Node: Automatic Reverting of File-Visiting Buffers38501 +Node: Risk of Reverting Automatically43486 +Node: Sections45868 +Node: Section Movement46794 +Node: Section Visibility51668 +Node: Section Hooks58355 +Node: Section Types and Values60761 +Node: Section Options62176 +Node: Transient Commands62647 +Node: Transient Arguments and Buffer Variables64123 +Node: Completion Confirmation and the Selection71140 +Node: Action Confirmation71586 +Node: Completion and Confirmation80091 +Node: The Selection83276 +Node: The hunk-internal region86174 +Node: Support for Completion Frameworks87263 +Node: Additional Completion Options92148 +Node: Mouse Support92746 +Node: Running Git93322 +Node: Viewing Git Output93567 +Node: Git Process Status96271 +Node: Running Git Manually97236 +Node: Git Executable99926 +Node: Global Git Arguments102934 +Node: Inspecting103739 +Node: Status Buffer104896 +Node: Status Sections109907 +Node: Status Header Sections115434 +Node: Status Module Sections118053 +Node: Status Options120550 +Node: Repository List121913 +Node: Logging126691 +Node: Refreshing Logs129530 +Node: Log Buffer130951 +Node: Log Margin135774 +Node: Select from Log138927 +Node: Reflog141137 +Node: Cherries142774 +Node: Diffing144612 +Node: Refreshing Diffs147646 +Node: Commands Available in Diffs151335 +Node: Diff Options153848 +Node: Revision Buffer159311 +Node: Ediffing162631 +Node: References Buffer168681 +Node: References Sections179275 +Node: Bisecting180132 +Node: Visiting Files and Blobs182443 +Node: General-Purpose Visit Commands182971 +Node: Visiting Files and Blobs from a Diff183924 +Node: Blaming187368 +Node: Manipulating194356 +Node: Creating Repository194698 +Node: Cloning Repository195235 +Node: Staging and Unstaging201676 +Node: Staging from File-Visiting Buffers205657 +Node: Applying206763 +Node: Committing208836 +Node: Initiating a Commit209419 +Node: Editing Commit Messages214609 +Node: Using the Revision Stack217382 +Node: Commit Pseudo Headers220427 +Node: Commit Mode and Hooks221722 +Node: Commit Message Conventions224580 +Node: Branching226567 +Node: The Two Remotes226793 +Node: Branch Commands229446 +Node: Branch Git Variables242296 +Node: Auxiliary Branch Commands247666 +Node: Merging248782 +Node: Resolving Conflicts252938 +Node: Rebasing258312 +Node: Editing Rebase Sequences263101 +Node: Information About In-Progress Rebase267317 +Ref: Information About In-Progress Rebase-Footnote-1276430 +Node: Cherry Picking277026 +Node: Reverting281360 +Node: Resetting282779 +Node: Stashing284605 +Node: Transferring290986 +Node: Remotes291208 +Node: Remote Commands291360 +Node: Remote Git Variables295399 +Node: Fetching296670 +Node: Pulling299153 +Node: Pushing300179 +Node: Plain Patches304470 +Node: Maildir Patches305941 +Node: Miscellaneous307420 +Node: Tagging307766 +Node: Notes309659 +Node: Submodules311994 +Node: Listing Submodules312212 +Node: Submodule Transient314360 +Node: Subtree316805 +Node: Worktree318736 +Node: Sparse checkouts319812 +Node: Bundle322588 +Node: Common Commands322963 +Node: Wip Modes325591 +Node: Wip Graph330482 +Node: Legacy Wip Modes332795 +Node: Commands for Buffers Visiting Files335682 +Node: Minor Mode for Buffers Visiting Blobs343909 +Node: Customizing344707 +Node: Per-Repository Configuration346303 +Node: Essential Settings348557 +Node: Safety348903 +Node: Performance350664 +Ref: Log Performance353627 +Ref: Diff Performance354932 +Ref: Refs Buffer Performance356273 +Ref: Committing Performance356848 +Node: Microsoft Windows Performance357830 +Node: MacOS Performance359021 +Ref: MacOS Performance-Footnote-1360044 +Node: Global Bindings360126 +Node: Plumbing362354 +Node: Calling Git363183 +Node: Getting a Value from Git364708 +Node: Calling Git for Effect368436 +Node: Section Plumbing374330 +Node: Creating Sections374558 +Node: Section Selection378454 +Node: Matching Sections380250 +Node: Refreshing Buffers386171 +Node: Conventions389315 +Node: Theming Faces389507 +Node: FAQ397612 +Node: FAQ - How to ...?398050 +Node: How to pronounce Magit?398407 +Node: How to show git's output?399210 +Node: How to install the gitman info manual?399996 +Node: How to show diffs for gpg-encrypted files?400966 +Node: How does branching and pushing work?401562 +Node: Should I disable VC?401895 +Node: FAQ - Issues and Errors402498 +Node: Magit is slow403443 +Node: I changed several thousand files at once and now Magit is unusable403736 +Node: I am having problems committing404462 +Node: I am using MS Windows and cannot push with Magit404943 +Node: I am using macOS and SOMETHING works in shell but not in Magit405561 +Node: Expanding a file to show the diff causes it to disappear406395 +Node: Point is wrong in the COMMIT_EDITMSG buffer406983 +Node: The mode-line information isn't always up-to-date408031 +Node: A branch and tag sharing the same name breaks SOMETHING409094 +Node: My Git hooks work on the command-line but not inside Magit409981 +Node: git-commit-mode isn't used when committing from the command-line410827 +Node: Point ends up inside invisible text when jumping to a file-visiting buffer413098 +Node: I am no longer able to save popup defaults413947 +Node: Debugging Tools414928 +Node: Keystroke Index418102 +Node: Function and Command Index459716 +Node: Variable Index517917 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/emacs/elpa/orderless-20240401.959/orderless-kwd.elc b/emacs/elpa/orderless-20240401.959/orderless-kwd.elc Binary files differ. diff --git a/emacs/elpa/orderless-20240401.959/orderless-pkg.el b/emacs/elpa/orderless-20240401.959/orderless-pkg.el @@ -1,14 +0,0 @@ -(define-package "orderless" "20240401.959" "Completion style for matching regexps in any order" - '((emacs "27.1")) - :commit "ac4aeb66f331f4c4a430d5556071e33177304c37" :authors - '(("Omar Antolín Camarena" . "omar@matem.unam.mx")) - :maintainers - '(("Omar Antolín Camarena <omar@matem.unam.mx>, Daniel Mendler" . "mail@daniel-mendler.de")) - :maintainer - '("Omar Antolín Camarena <omar@matem.unam.mx>, Daniel Mendler" . "mail@daniel-mendler.de") - :keywords - '("extensions") - :url "https://github.com/oantolin/orderless") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/orderless-20240401.959/orderless.el b/emacs/elpa/orderless-20240401.959/orderless.el @@ -1,661 +0,0 @@ -;;; orderless.el --- Completion style for matching regexps in any order -*- lexical-binding: t; -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; Author: Omar Antolín Camarena <omar@matem.unam.mx> -;; Maintainer: Omar Antolín Camarena <omar@matem.unam.mx>, Daniel Mendler <mail@daniel-mendler.de> -;; Keywords: extensions -;; Version: 1.1 -;; Homepage: https://github.com/oantolin/orderless -;; Package-Requires: ((emacs "27.1")) - -;; This file is part of GNU Emacs. - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see <https://www.gnu.org/licenses/>. - -;;; Commentary: - -;; This package provides an `orderless' completion style that divides -;; the pattern into components (space-separated by default), and -;; matches candidates that match all of the components in any order. - -;; Completion styles are used as entries in the variables -;; `completion-styles' and `completion-category-overrides', see their -;; documentation. - -;; To use this completion style you can use the following minimal -;; configuration: - -;; (setq completion-styles '(orderless basic)) - -;; You can customize the `orderless-component-separator' to decide how -;; the input pattern is split into component regexps. The default -;; splits on spaces. You might want to add hyphens and slashes, for -;; example, to ease completion of symbols and file paths, -;; respectively. - -;; Each component can match in any one of several matching styles: -;; literally, as a regexp, as an initialism, in the flex style, or as -;; word prefixes. It is easy to add new styles: they are functions -;; from strings to strings that map a component to a regexp to match -;; against. The variable `orderless-matching-styles' lists the -;; matching styles to be used for components, by default it allows -;; literal and regexp matching. - -;;; Code: - -(eval-when-compile (require 'cl-lib)) - -(defgroup orderless nil - "Completion method that matches space-separated regexps in any order." - :group 'minibuffer) - -(defface orderless-match-face-0 - '((default :weight bold) - (((class color) (min-colors 88) (background dark)) :foreground "#72a4ff") - (((class color) (min-colors 88) (background light)) :foreground "#223fbf") - (t :foreground "blue")) - "Face for matches of components numbered 0 mod 4.") - -(defface orderless-match-face-1 - '((default :weight bold) - (((class color) (min-colors 88) (background dark)) :foreground "#ed92f8") - (((class color) (min-colors 88) (background light)) :foreground "#8f0075") - (t :foreground "magenta")) - "Face for matches of components numbered 1 mod 4.") - -(defface orderless-match-face-2 - '((default :weight bold) - (((class color) (min-colors 88) (background dark)) :foreground "#90d800") - (((class color) (min-colors 88) (background light)) :foreground "#145a00") - (t :foreground "green")) - "Face for matches of components numbered 2 mod 4.") - -(defface orderless-match-face-3 - '((default :weight bold) - (((class color) (min-colors 88) (background dark)) :foreground "#f0ce43") - (((class color) (min-colors 88) (background light)) :foreground "#804000") - (t :foreground "yellow")) - "Face for matches of components numbered 3 mod 4.") - -(defcustom orderless-component-separator #'orderless-escapable-split-on-space - "Component separators for orderless completion. -This can either be a string, which is passed to `split-string', -or a function of a single string argument." - :type `(choice (const :tag "Spaces" " +") - (const :tag "Spaces, hyphen or slash" " +\\|[-/]") - (const :tag "Escapable space" - ,#'orderless-escapable-split-on-space) - (const :tag "Quotable spaces" ,#'split-string-and-unquote) - (regexp :tag "Custom regexp") - (function :tag "Custom function"))) - -(defcustom orderless-match-faces - [orderless-match-face-0 - orderless-match-face-1 - orderless-match-face-2 - orderless-match-face-3] - "Vector of faces used (cyclically) for component matches." - :type '(vector face)) - -(defcustom orderless-matching-styles - (list #'orderless-literal #'orderless-regexp) - "List of component matching styles. -If this variable is nil, regexp matching is assumed. - -A matching style is simply a function from strings to regexps. -The returned regexps can be either strings or s-expressions in -`rx' syntax. If the resulting regexp has no capturing groups, -the entire match is highlighted, otherwise just the captured -groups are. Several are provided with this package: try -customizing this variable to see a list of them." - :type 'hook - :options (list #'orderless-regexp - #'orderless-literal - #'orderless-initialism - #'orderless-prefixes - #'orderless-flex)) - -(defcustom orderless-affix-dispatch-alist - `((?% . ,#'char-fold-to-regexp) - (?! . ,#'orderless-not) - (?& . ,#'orderless-annotation) - (?, . ,#'orderless-initialism) - (?= . ,#'orderless-literal) - (?^ . ,#'orderless-literal-prefix) - (?~ . ,#'orderless-flex)) - "Alist associating characters to matching styles. -The function `orderless-affix-dispatch' uses this list to -determine how to match a pattern component: if the component -either starts or ends with a character used as a key in this -alist, the character is removed from the component and the rest is -matched according the style associated to it." - :type `(alist - :key-type character - :value-type (choice - (const :tag "Annotation" ,#'orderless-annotation) - (const :tag "Literal" ,#'orderless-literal) - (const :tag "Without literal" ,#'orderless-without-literal) - (const :tag "Literal prefix" ,#'orderless-literal-prefix) - (const :tag "Regexp" ,#'orderless-regexp) - (const :tag "Not" ,#'orderless-not) - (const :tag "Flex" ,#'orderless-flex) - (const :tag "Initialism" ,#'orderless-initialism) - (const :tag "Prefixes" ,#'orderless-prefixes) - (const :tag "Ignore diacritics" ,#'char-fold-to-regexp) - (function :tag "Custom matching style")))) - -(defun orderless-affix-dispatch (component _index _total) - "Match COMPONENT according to the styles in `orderless-affix-dispatch-alist'. -If the COMPONENT starts or ends with one of the characters used -as a key in `orderless-affix-dispatch-alist', then that character -is removed and the remainder of the COMPONENT is matched in the -style associated to the character." - (let ((len (length component)) - (alist orderless-affix-dispatch-alist)) - (when (> len 0) - (cond - ;; Ignore single dispatcher character - ((and (= len 1) (alist-get (aref component 0) alist)) #'ignore) - ;; Prefix - ((when-let ((style (alist-get (aref component 0) alist))) - (cons style (substring component 1)))) - ;; Suffix - ((when-let ((style (alist-get (aref component (1- len)) alist))) - (cons style (substring component 0 -1)))))))) - -(defcustom orderless-style-dispatchers (list #'orderless-affix-dispatch) - "List of style dispatchers. -Style dispatchers are used to override the matching styles -based on the actual component and its place in the list of -components. A style dispatcher is a function that takes a string -and two integers as arguments, it gets called with a component, -the 0-based index of the component and the total number of -components. It can decide what matching styles to use for the -component and optionally replace the component with a different -string, or it can decline to handle the component leaving it for -future dispatchers. For details see `orderless--dispatch'. - -For example, a style dispatcher could arrange for the first -component to match as an initialism and subsequent components to -match as literals. As another example, a style dispatcher could -arrange for a component starting with `~' to match the rest of -the component in the `orderless-flex' style. See -`orderless-affix-dispatch' and `orderless-affix-dispatch-alist' -for such a configuration. For more information on how this -variable is used, see `orderless-compile'." - :type 'hook) - -(defcustom orderless-smart-case t - "Whether to use smart case. -If this variable is t, then case-sensitivity is decided as -follows: if any component contains upper case letters, the -matches are case sensitive; otherwise case-insensitive. This -is like the behavior of `isearch' when `search-upper-case' is -non-nil. - -On the other hand, if this variable is nil, then case-sensitivity -is determined by the values of `completion-ignore-case', -`read-file-name-completion-ignore-case' and -`read-buffer-completion-ignore-case', as usual for completion." - :type 'boolean) - -;;; Matching styles - -(defun orderless-regexp (component) - "Match COMPONENT as a regexp." - (condition-case nil - (progn (string-match-p component "") component) - (invalid-regexp nil))) - -(defun orderless-literal (component) - "Match COMPONENT as a literal string." - ;; Do not use (literal component) here, such that `delete-dups' in - ;; `orderless--compile-component' has a chance to delete duplicates for - ;; literal input. The default configuration of `orderless-matching-styles' - ;; with `orderless-regexp' and `orderless-literal' leads to duplicates. - (regexp-quote component)) - -(defun orderless-literal-prefix (component) - "Match COMPONENT as a literal prefix string." - `(seq bos (literal ,component))) - -(defun orderless--separated-by (sep rxs &optional before after) - "Return a regexp to match the rx-regexps RXS with SEP in between. -If BEFORE is specified, add it to the beginning of the rx -sequence. If AFTER is specified, add it to the end of the rx -sequence." - (declare (indent 1)) - `(seq - ,(or before "") - ,@(cl-loop for (sexp . more) on rxs - collect `(group ,sexp) - when more collect sep) - ,(or after ""))) - -(defun orderless-flex (component) - "Match a component in flex style. -This means the characters in COMPONENT must occur in the -candidate in that order, but not necessarily consecutively." - `(seq - ,@(cdr (cl-loop for char across component - append `((zero-or-more (not ,char)) (group ,char)))))) - -(defun orderless-initialism (component) - "Match a component as an initialism. -This means the characters in COMPONENT must occur in the -candidate, in that order, at the beginning of words." - (orderless--separated-by '(zero-or-more nonl) - (cl-loop for char across component collect `(seq word-start ,char)))) - -(defun orderless-prefixes (component) - "Match a component as multiple word prefixes. -The COMPONENT is split at word endings, and each piece must match -at a word boundary in the candidate. This is similar to the -`partial-completion' completion style." - (orderless--separated-by '(zero-or-more nonl) - (cl-loop for prefix in (split-string component "\\>") - collect `(seq word-boundary ,prefix)))) - -(defun orderless-without-literal (component) - "Match strings that do *not* contain COMPONENT as a literal match. -You may prefer to use the more general `orderless-not' instead -which can invert any predicate or regexp." - `(seq - (group string-start) ; highlight nothing! - (zero-or-more - (or ,@(cl-loop for i below (length component) - collect `(seq ,(substring component 0 i) - (or (not (any ,(aref component i))) - string-end))))) - string-end)) - -(defsubst orderless--match-p (pred regexp str) - "Return t if STR matches PRED and REGEXP." - (and str - (or (not pred) (funcall pred str)) - (or (not regexp) (string-match-p regexp str)))) - -(defun orderless-not (pred regexp) - "Match strings that do *not* match PRED and REGEXP." - (lambda (str) - (not (orderless--match-p pred regexp str)))) - -(defun orderless--metadata () - "Return completion metadata iff inside minibuffer." - (when-let (((minibufferp)) - (table minibuffer-completion-table)) - ;; Return non-nil metadata iff inside minibuffer - (or (completion-metadata (buffer-substring-no-properties - (minibuffer-prompt-end) (point)) - table minibuffer-completion-predicate) - '((nil . nil))))) - -(defun orderless-annotation (pred regexp) - "Match candidates where the annotation matches PRED and REGEXP." - (when-let ((metadata (orderless--metadata)) - (fun (or (completion-metadata-get - metadata 'annotation-function) - (plist-get completion-extra-properties - :annotation-function) - (when-let ((aff (or (completion-metadata-get - metadata 'affixation-function) - (plist-get completion-extra-properties - :affixation-function)))) - (lambda (cand) (caddr (funcall aff (list cand)))))))) - (lambda (str) - (orderless--match-p pred regexp (funcall fun str))))) - -;;; Highlighting matches - -(defun orderless--highlight (regexps ignore-case string) - "Destructively propertize STRING to highlight a match of each of the REGEXPS. -The search is case insensitive if IGNORE-CASE is non-nil." - (cl-loop with case-fold-search = ignore-case - with n = (length orderless-match-faces) - for regexp in regexps and i from 0 - when (string-match regexp string) do - (cl-loop - for (x y) on (let ((m (match-data))) (or (cddr m) m)) by #'cddr - when x do - (add-face-text-property - x y - (aref orderless-match-faces (mod i n)) - nil string))) - string) - -(defun orderless-highlight-matches (regexps strings) - "Highlight a match of each of the REGEXPS in each of the STRINGS. -Warning: only use this if you know all REGEXPs match all STRINGS! -For the user's convenience, if REGEXPS is a string, it is -converted to a list of regexps according to the value of -`orderless-matching-styles'." - (when (stringp regexps) - (setq regexps (cdr (orderless-compile regexps)))) - (cl-loop with ignore-case = (orderless--ignore-case-p regexps) - for str in strings - collect (orderless--highlight regexps ignore-case (substring str)))) - -;;; Compiling patterns to lists of regexps - -(defun orderless-escapable-split-on-space (string) - "Split STRING on spaces, which can be escaped with backslash." - (mapcar - (lambda (piece) (replace-regexp-in-string (string 0) " " piece)) - (split-string (replace-regexp-in-string - "\\\\\\\\\\|\\\\ " - (lambda (x) (if (equal x "\\ ") (string 0) x)) - string 'fixedcase 'literal) - " +" t))) - -(define-obsolete-function-alias 'orderless-dispatch 'orderless--dispatch "1.0") -(defun orderless--dispatch (dispatchers default string index total) - "Run DISPATCHERS to compute matching styles for STRING. - -A style dispatcher is a function that takes a STRING, component -INDEX and the TOTAL number of components. It should either -return (a) nil to indicate the dispatcher will not handle the -string, (b) a new string to replace the current string and -continue dispatch, or (c) the matching styles to use and, if -needed, a new string to use in place of the current one (for -example, a dispatcher can decide which style to use based on a -suffix of the string and then it must also return the component -stripped of the suffix). - -More precisely, the return value of a style dispatcher can be of -one of the following forms: - -- nil (to continue dispatching) - -- a string (to replace the component and continue dispatching), - -- a matching style or non-empty list of matching styles to - return, - -- a `cons' whose `car' is either as in the previous case or - nil (to request returning the DEFAULT matching styles), and - whose `cdr' is a string (to replace the current one). - -This function tries all DISPATCHERS in sequence until one returns -a list of styles. When that happens it returns a `cons' of the -list of styles and the possibly updated STRING. If none of the -DISPATCHERS returns a list of styles, the return value will use -DEFAULT as the list of styles." - (cl-loop for dispatcher in dispatchers - for result = (funcall dispatcher string index total) - if (stringp result) - do (setq string result result nil) - else if (and (consp result) (null (car result))) - do (setf (car result) default) - else if (and (consp result) (stringp (cdr result))) - do (setq string (cdr result) result (car result)) - when result return (cons result string) - finally (return (cons default string)))) - -(defun orderless--compile-component (component index total styles dispatchers) - "Compile COMPONENT at INDEX of TOTAL components with STYLES and DISPATCHERS." - (cl-loop - with pred = nil - with (newsty . newcomp) = (orderless--dispatch dispatchers styles - component index total) - for style in (if (functionp newsty) (list newsty) newsty) - for res = (condition-case nil - (funcall style newcomp) - (wrong-number-of-arguments - (when-let ((res (orderless--compile-component - newcomp index total styles dispatchers))) - (funcall style (car res) (cdr res))))) - if (functionp res) do (cl-callf orderless--predicate-and pred res) - else if res collect (if (stringp res) `(regexp ,res) res) into regexps - finally return - (when (or pred regexps) - (cons pred (and regexps (rx-to-string `(or ,@(delete-dups regexps)) t)))))) - -(defun orderless-compile (pattern &optional styles dispatchers) - "Build regexps to match the components of PATTERN. -Split PATTERN on `orderless-component-separator' and compute -matching styles for each component. For each component the style -DISPATCHERS are run to determine the matching styles to be used; -they are called with arguments the component, the 0-based index -of the component and the total number of components. If the -DISPATCHERS decline to handle the component, then the list of -matching STYLES is used. See `orderless--dispatch' for details -on dispatchers. - -The STYLES default to `orderless-matching-styles', and the -DISPATCHERS default to `orderless-dipatchers'. Since nil gets -you the default, if you want no dispatchers to be run, use -\\='(ignore) as the value of DISPATCHERS. - -The return value is a pair of a predicate function and a list of -regexps. The predicate function can also be nil. It takes a -string as argument." - (unless styles (setq styles orderless-matching-styles)) - (unless dispatchers (setq dispatchers orderless-style-dispatchers)) - (cl-loop - with predicate = nil - with components = (if (functionp orderless-component-separator) - (funcall orderless-component-separator pattern) - (split-string pattern orderless-component-separator t)) - with total = (length components) - for comp in components and index from 0 - for (pred . regexp) = (orderless--compile-component - comp index total styles dispatchers) - when regexp collect regexp into regexps - when pred do (cl-callf orderless--predicate-and predicate pred) - finally return (cons predicate regexps))) - -(defun orderless-pattern-compiler (pattern &optional styles dispatchers) - "Obsolete function, use `orderless-compile' instead. -See `orderless-compile' for the arguments PATTERN, STYLES and DISPATCHERS." - (cdr (orderless-compile pattern styles dispatchers))) -(make-obsolete 'orderless-pattern-compiler 'orderless-compile "1.0") - -;;; Completion style implementation - -(defun orderless--predicate-normalized-and (p q) - "Combine two predicate functions P and Q with `and'. -The first function P is a completion predicate which can receive -up to two arguments. The second function Q always receives a -normalized string as argument." - (cond - ((and p q) - (lambda (k &rest v) ;; v for hash table - (when (if v (funcall p k (car v)) (funcall p k)) - (setq k (if (consp k) (car k) k)) ;; alist - (funcall q (if (symbolp k) (symbol-name k) k))))) - (q - (lambda (k &optional _) ;; _ for hash table - (setq k (if (consp k) (car k) k)) ;; alist - (funcall q (if (symbolp k) (symbol-name k) k)))) - (p))) - -(defun orderless--predicate-and (p q) - "Combine two predicate functions P and Q with `and'." - (or (and p q (lambda (x) (and (funcall p x) (funcall q x)))) p q)) - -(defun orderless--compile (string table pred) - "Compile STRING to a prefix and a list of regular expressions. -The predicate PRED is used to constrain the entries in TABLE." - (pcase-let* ((limit (car (completion-boundaries string table pred ""))) - (prefix (substring string 0 limit)) - (pattern (substring string limit)) - (`(,fun . ,regexps) (orderless-compile pattern))) - (list prefix regexps (orderless--ignore-case-p regexps) - (orderless--predicate-normalized-and pred fun)))) - -;; Thanks to @jakanakaevangeli for writing a version of this function: -;; https://github.com/oantolin/orderless/issues/79#issuecomment-916073526 -(defun orderless--literal-prefix-p (regexp) - "Determine if REGEXP is a quoted regexp anchored at the beginning. -If REGEXP is of the form \"\\`q\" for q = (regexp-quote u), -then return (cons REGEXP u); else return nil." - (when (and (string-prefix-p "\\`" regexp) - (not (string-match-p "[$*+.?[\\^]" - (replace-regexp-in-string - "\\\\[$*+.?[\\^]" "" regexp - 'fixedcase 'literal nil 2)))) - (cons regexp - (replace-regexp-in-string "\\\\\\([$*+.?[\\^]\\)" "\\1" - regexp 'fixedcase nil nil 2)))) - -(defun orderless--ignore-case-p (regexps) - "Return non-nil if case should be ignored for REGEXPS." - (if orderless-smart-case - (cl-loop for regexp in regexps - always (isearch-no-upper-case-p regexp t)) - completion-ignore-case)) - -(defun orderless--filter (prefix regexps ignore-case table pred) - "Filter TABLE by PREFIX, REGEXPS and PRED. -The matching should be case-insensitive if IGNORE-CASE is non-nil." - ;; If there is a regexp of the form \`quoted-regexp then - ;; remove the first such and add the unquoted form to the prefix. - (pcase (cl-loop for r in regexps - thereis (orderless--literal-prefix-p r)) - (`(,regexp . ,literal) - (setq prefix (concat prefix literal) - regexps (remove regexp regexps)))) - (let ((completion-regexp-list regexps) - (completion-ignore-case ignore-case)) - (all-completions prefix table pred))) - -(defun orderless-filter (string table &optional pred) - "Split STRING into components and find entries TABLE matching all. -The predicate PRED is used to constrain the entries in TABLE." - (pcase-let ((`(,prefix ,regexps ,ignore-case ,pred) - (orderless--compile string table pred))) - (orderless--filter prefix regexps ignore-case table pred))) - -;;;###autoload -(defun orderless-all-completions (string table pred _point) - "Split STRING into components and find entries TABLE matching all. -The predicate PRED is used to constrain the entries in TABLE. The -matching portions of each candidate are highlighted. -This function is part of the `orderless' completion style." - (defvar completion-lazy-hilit-fn) - (pcase-let ((`(,prefix ,regexps ,ignore-case ,pred) - (orderless--compile string table pred))) - (when-let ((completions (orderless--filter prefix regexps ignore-case table pred))) - (if (bound-and-true-p completion-lazy-hilit) - (setq completion-lazy-hilit-fn - (apply-partially #'orderless--highlight regexps ignore-case)) - (cl-loop for str in-ref completions do - (setf str (orderless--highlight regexps ignore-case (substring str))))) - (nconc completions (length prefix))))) - -;;;###autoload -(defun orderless-try-completion (string table pred point) - "Complete STRING to unique matching entry in TABLE. -This uses `orderless-all-completions' to find matches for STRING -in TABLE among entries satisfying PRED. If there is only one -match, it completes to that match. If there are no matches, it -returns nil. In any other case it \"completes\" STRING to -itself, without moving POINT. -This function is part of the `orderless' completion style." - (catch 'orderless--many - (pcase-let ((`(,prefix ,regexps ,ignore-case ,pred) - (orderless--compile string table pred)) - (one nil)) - ;; Abuse all-completions/orderless--filter as a fast search loop. - ;; Should be almost allocation-free since our "predicate" is not - ;; called more than two times. - (orderless--filter - prefix regexps ignore-case table - (orderless--predicate-normalized-and - pred - (lambda (arg) - ;; Check if there is more than a single match (= many). - (when (and one (not (equal one arg))) - (throw 'orderless--many (cons string point))) - (setq one arg) - t))) - (when one - ;; Prepend prefix if the candidate does not already have the same - ;; prefix. This workaround is needed since the predicate may either - ;; receive an unprefixed or a prefixed candidate as argument. Most - ;; completion tables consistently call the predicate with unprefixed - ;; candidates, for example `completion-file-name-table'. In contrast, - ;; `completion-table-with-context' calls the predicate with prefixed - ;; candidates. This could be an unintended bug or oversight in - ;; `completion-table-with-context'. - (unless (or (equal prefix "") - (and (string-prefix-p prefix one) - (test-completion one table pred))) - (setq one (concat prefix one))) - (or (equal string one) ;; Return t for unique exact match - (cons one (length one))))))) - -;;;###autoload -(add-to-list 'completion-styles-alist - '(orderless - orderless-try-completion orderless-all-completions - "Completion of multiple components, in any order.")) - -(defmacro orderless-define-completion-style - (name &optional docstring &rest configuration) - "Define an orderless completion style with given CONFIGURATION. -The CONFIGURATION should be a list of bindings that you could use -with `let' to configure orderless. You can include bindings for -`orderless-matching-styles' and `orderless-style-dispatchers', -for example. - -The completion style consists of two functions that this macro -defines for you, NAME-try-completion and NAME-all-completions. -This macro registers those in `completion-styles-alist' as -forming the completion style NAME. - -The optional DOCSTRING argument is used as the documentation -string for the completion style." - (declare (doc-string 2) (indent 1)) - (unless (stringp docstring) - (push docstring configuration) - (setq docstring nil)) - (let* ((fn-name (lambda (string) (intern (concat (symbol-name name) string)))) - (try-completion (funcall fn-name "-try-completion")) - (all-completions (funcall fn-name "-all-completions")) - (doc-fmt "`%s' function for the %s style. -This function delegates to `orderless-%s'. -The orderless configuration is locally modified -specifically for the %s style.") - (fn-doc (lambda (fn) (format doc-fmt fn name fn name name)))) - `(progn - (defun ,try-completion (string table pred point) - ,(funcall fn-doc "try-completion") - (let ,configuration - (orderless-try-completion string table pred point))) - (defun ,all-completions (string table pred point) - ,(funcall fn-doc "all-completions") - (let ,configuration - (orderless-all-completions string table pred point))) - (add-to-list 'completion-styles-alist - '(,name ,try-completion ,all-completions ,docstring))))) - -;;; Ivy integration - -;;;###autoload -(defun orderless-ivy-re-builder (str) - "Convert STR into regexps for use with ivy. -This function is for integration of orderless with ivy, use it as -a value in `ivy-re-builders-alist'." - (or (mapcar (lambda (x) (cons x t)) (cdr (orderless-compile str))) "")) - -(defvar ivy-regex) -(defun orderless-ivy-highlight (str) - "Highlight a match in STR of each regexp in `ivy-regex'. -This function is for integration of orderless with ivy." - (orderless--highlight (mapcar #'car ivy-regex) t str) str) - -(provide 'orderless) -;;; orderless.el ends here diff --git a/emacs/elpa/orderless-20240401.959/orderless.elc b/emacs/elpa/orderless-20240401.959/orderless.elc Binary files differ. diff --git a/emacs/elpa/orderless-20240401.959/dir b/emacs/elpa/orderless-20240606.1026/dir diff --git a/emacs/elpa/orderless-20240401.959/orderless-autoloads.el b/emacs/elpa/orderless-20240606.1026/orderless-autoloads.el diff --git a/emacs/elpa/orderless-20240401.959/orderless-kwd.el b/emacs/elpa/orderless-20240606.1026/orderless-kwd.el diff --git a/emacs/elpa/orderless-20240606.1026/orderless-kwd.elc b/emacs/elpa/orderless-20240606.1026/orderless-kwd.elc Binary files differ. diff --git a/emacs/elpa/orderless-20240606.1026/orderless-pkg.el b/emacs/elpa/orderless-20240606.1026/orderless-pkg.el @@ -0,0 +1,14 @@ +(define-package "orderless" "20240606.1026" "Completion style for matching regexps in any order" + '((emacs "27.1")) + :commit "53f5204ad3f541e11eb6eeb9b86584964b7a3678" :authors + '(("Omar Antolín Camarena" . "omar@matem.unam.mx")) + :maintainers + '(("Omar Antolín Camarena <omar@matem.unam.mx>, Daniel Mendler" . "mail@daniel-mendler.de")) + :maintainer + '("Omar Antolín Camarena <omar@matem.unam.mx>, Daniel Mendler" . "mail@daniel-mendler.de") + :keywords + '("extensions") + :url "https://github.com/oantolin/orderless") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs/elpa/orderless-20240606.1026/orderless.el b/emacs/elpa/orderless-20240606.1026/orderless.el @@ -0,0 +1,663 @@ +;;; orderless.el --- Completion style for matching regexps in any order -*- lexical-binding: t; -*- + +;; Copyright (C) 2021-2024 Free Software Foundation, Inc. + +;; Author: Omar Antolín Camarena <omar@matem.unam.mx> +;; Maintainer: Omar Antolín Camarena <omar@matem.unam.mx>, Daniel Mendler <mail@daniel-mendler.de> +;; Keywords: extensions +;; Version: 1.1 +;; Homepage: https://github.com/oantolin/orderless +;; Package-Requires: ((emacs "27.1")) + +;; This file is part of GNU Emacs. + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This package provides an `orderless' completion style that divides +;; the pattern into components (space-separated by default), and +;; matches candidates that match all of the components in any order. + +;; Completion styles are used as entries in the variables +;; `completion-styles' and `completion-category-overrides', see their +;; documentation. + +;; To use this completion style you can use the following minimal +;; configuration: + +;; (setq completion-styles '(orderless basic)) + +;; You can customize the `orderless-component-separator' to decide how +;; the input pattern is split into component regexps. The default +;; splits on spaces. You might want to add hyphens and slashes, for +;; example, to ease completion of symbols and file paths, +;; respectively. + +;; Each component can match in any one of several matching styles: +;; literally, as a regexp, as an initialism, in the flex style, or as +;; word prefixes. It is easy to add new styles: they are functions +;; from strings to strings that map a component to a regexp to match +;; against. The variable `orderless-matching-styles' lists the +;; matching styles to be used for components, by default it allows +;; literal and regexp matching. + +;;; Code: + +(eval-when-compile (require 'cl-lib)) + +(defgroup orderless nil + "Completion method that matches space-separated regexps in any order." + :group 'minibuffer) + +(defface orderless-match-face-0 + '((default :weight bold) + (((class color) (min-colors 88) (background dark)) :foreground "#72a4ff") + (((class color) (min-colors 88) (background light)) :foreground "#223fbf") + (t :foreground "blue")) + "Face for matches of components numbered 0 mod 4.") + +(defface orderless-match-face-1 + '((default :weight bold) + (((class color) (min-colors 88) (background dark)) :foreground "#ed92f8") + (((class color) (min-colors 88) (background light)) :foreground "#8f0075") + (t :foreground "magenta")) + "Face for matches of components numbered 1 mod 4.") + +(defface orderless-match-face-2 + '((default :weight bold) + (((class color) (min-colors 88) (background dark)) :foreground "#90d800") + (((class color) (min-colors 88) (background light)) :foreground "#145a00") + (t :foreground "green")) + "Face for matches of components numbered 2 mod 4.") + +(defface orderless-match-face-3 + '((default :weight bold) + (((class color) (min-colors 88) (background dark)) :foreground "#f0ce43") + (((class color) (min-colors 88) (background light)) :foreground "#804000") + (t :foreground "yellow")) + "Face for matches of components numbered 3 mod 4.") + +(defcustom orderless-component-separator #'orderless-escapable-split-on-space + "Component separators for orderless completion. +This can either be a string, which is passed to `split-string', +or a function of a single string argument." + :type `(choice (const :tag "Spaces" " +") + (const :tag "Spaces, hyphen or slash" " +\\|[-/]") + (const :tag "Escapable space" + ,#'orderless-escapable-split-on-space) + (const :tag "Quotable spaces" ,#'split-string-and-unquote) + (regexp :tag "Custom regexp") + (function :tag "Custom function"))) + +(defcustom orderless-match-faces + [orderless-match-face-0 + orderless-match-face-1 + orderless-match-face-2 + orderless-match-face-3] + "Vector of faces used (cyclically) for component matches." + :type '(vector face)) + +(defcustom orderless-matching-styles + (list #'orderless-literal #'orderless-regexp) + "List of component matching styles. +If this variable is nil, regexp matching is assumed. + +A matching style is simply a function from strings to regexps. +The returned regexps can be either strings or s-expressions in +`rx' syntax. If the resulting regexp has no capturing groups, +the entire match is highlighted, otherwise just the captured +groups are. Several are provided with this package: try +customizing this variable to see a list of them." + :type 'hook + :options (list #'orderless-regexp + #'orderless-literal + #'orderless-initialism + #'orderless-prefixes + #'orderless-flex)) + +(defcustom orderless-affix-dispatch-alist + `((?% . ,#'char-fold-to-regexp) + (?! . ,#'orderless-not) + (?& . ,#'orderless-annotation) + (?, . ,#'orderless-initialism) + (?= . ,#'orderless-literal) + (?^ . ,#'orderless-literal-prefix) + (?~ . ,#'orderless-flex)) + "Alist associating characters to matching styles. +The function `orderless-affix-dispatch' uses this list to +determine how to match a pattern component: if the component +either starts or ends with a character used as a key in this +alist, the character is removed from the component and the rest is +matched according the style associated to it." + :type `(alist + :key-type character + :value-type (choice + (const :tag "Annotation" ,#'orderless-annotation) + (const :tag "Literal" ,#'orderless-literal) + (const :tag "Without literal" ,#'orderless-without-literal) + (const :tag "Literal prefix" ,#'orderless-literal-prefix) + (const :tag "Regexp" ,#'orderless-regexp) + (const :tag "Not" ,#'orderless-not) + (const :tag "Flex" ,#'orderless-flex) + (const :tag "Initialism" ,#'orderless-initialism) + (const :tag "Prefixes" ,#'orderless-prefixes) + (const :tag "Ignore diacritics" ,#'char-fold-to-regexp) + (function :tag "Custom matching style")))) + +(defun orderless-affix-dispatch (component _index _total) + "Match COMPONENT according to the styles in `orderless-affix-dispatch-alist'. +If the COMPONENT starts or ends with one of the characters used +as a key in `orderless-affix-dispatch-alist', then that character +is removed and the remainder of the COMPONENT is matched in the +style associated to the character." + (let ((len (length component)) + (alist orderless-affix-dispatch-alist)) + (when (> len 0) + (cond + ;; Ignore single dispatcher character + ((and (= len 1) (alist-get (aref component 0) alist)) #'ignore) + ;; Prefix + ((when-let ((style (alist-get (aref component 0) alist))) + (cons style (substring component 1)))) + ;; Suffix + ((when-let ((style (alist-get (aref component (1- len)) alist))) + (cons style (substring component 0 -1)))))))) + +(defcustom orderless-style-dispatchers (list #'orderless-affix-dispatch) + "List of style dispatchers. +Style dispatchers are used to override the matching styles +based on the actual component and its place in the list of +components. A style dispatcher is a function that takes a string +and two integers as arguments, it gets called with a component, +the 0-based index of the component and the total number of +components. It can decide what matching styles to use for the +component and optionally replace the component with a different +string, or it can decline to handle the component leaving it for +future dispatchers. For details see `orderless--dispatch'. + +For example, a style dispatcher could arrange for the first +component to match as an initialism and subsequent components to +match as literals. As another example, a style dispatcher could +arrange for a component starting with `~' to match the rest of +the component in the `orderless-flex' style. See +`orderless-affix-dispatch' and `orderless-affix-dispatch-alist' +for such a configuration. For more information on how this +variable is used, see `orderless-compile'." + :type 'hook) + +(defcustom orderless-smart-case t + "Whether to use smart case. +If this variable is t, then case-sensitivity is decided as +follows: if any component contains upper case letters, the +matches are case sensitive; otherwise case-insensitive. This +is like the behavior of `isearch' when `search-upper-case' is +non-nil. + +On the other hand, if this variable is nil, then case-sensitivity +is determined by the values of `completion-ignore-case', +`read-file-name-completion-ignore-case' and +`read-buffer-completion-ignore-case', as usual for completion." + :type 'boolean) + +;;; Matching styles + +(defun orderless-regexp (component) + "Match COMPONENT as a regexp." + (condition-case nil + (progn (string-match-p component "") component) + (invalid-regexp nil))) + +(defun orderless-literal (component) + "Match COMPONENT as a literal string." + ;; Do not use (literal component) here, such that `delete-dups' in + ;; `orderless--compile-component' has a chance to delete duplicates for + ;; literal input. The default configuration of `orderless-matching-styles' + ;; with `orderless-regexp' and `orderless-literal' leads to duplicates. + (regexp-quote component)) + +(defun orderless-literal-prefix (component) + "Match COMPONENT as a literal prefix string." + `(seq bos (literal ,component))) + +(defun orderless--separated-by (sep rxs &optional before after) + "Return a regexp to match the rx-regexps RXS with SEP in between. +If BEFORE is specified, add it to the beginning of the rx +sequence. If AFTER is specified, add it to the end of the rx +sequence." + (declare (indent 1)) + `(seq + ,(or before "") + ,@(cl-loop for (sexp . more) on rxs + collect `(group ,sexp) + when more collect sep) + ,(or after ""))) + +(defun orderless-flex (component) + "Match a component in flex style. +This means the characters in COMPONENT must occur in the +candidate in that order, but not necessarily consecutively." + `(seq + ,@(cdr (cl-loop for char across component + append `((zero-or-more (not ,char)) (group ,char)))))) + +(defun orderless-initialism (component) + "Match a component as an initialism. +This means the characters in COMPONENT must occur in the +candidate, in that order, at the beginning of words." + (orderless--separated-by '(zero-or-more nonl) + (cl-loop for char across component collect `(seq word-start ,char)))) + +(defun orderless-prefixes (component) + "Match a component as multiple word prefixes. +The COMPONENT is split at word endings, and each piece must match +at a word boundary in the candidate. This is similar to the +`partial-completion' completion style." + (orderless--separated-by '(zero-or-more nonl) + (cl-loop for prefix in (split-string component "\\>") + collect `(seq word-boundary ,prefix)))) + +(defun orderless-without-literal (component) + "Match strings that do *not* contain COMPONENT as a literal match. +You may prefer to use the more general `orderless-not' instead +which can invert any predicate or regexp." + `(seq + (group string-start) ; highlight nothing! + (zero-or-more + (or ,@(cl-loop for i below (length component) + collect `(seq ,(substring component 0 i) + (or (not (any ,(aref component i))) + string-end))))) + string-end)) + +(defsubst orderless--match-p (pred regexp str) + "Return t if STR matches PRED and REGEXP." + (and str + (or (not pred) (funcall pred str)) + (or (not regexp) + (let ((case-fold-search completion-ignore-case)) + (string-match-p regexp str))))) + +(defun orderless-not (pred regexp) + "Match strings that do *not* match PRED and REGEXP." + (lambda (str) + (not (orderless--match-p pred regexp str)))) + +(defun orderless--metadata () + "Return completion metadata iff inside minibuffer." + (when-let (((minibufferp)) + (table minibuffer-completion-table)) + ;; Return non-nil metadata iff inside minibuffer + (or (completion-metadata (buffer-substring-no-properties + (minibuffer-prompt-end) (point)) + table minibuffer-completion-predicate) + '((nil . nil))))) + +(defun orderless-annotation (pred regexp) + "Match candidates where the annotation matches PRED and REGEXP." + (when-let ((metadata (orderless--metadata)) + (fun (or (completion-metadata-get + metadata 'annotation-function) + (plist-get completion-extra-properties + :annotation-function) + (when-let ((aff (or (completion-metadata-get + metadata 'affixation-function) + (plist-get completion-extra-properties + :affixation-function)))) + (lambda (cand) (caddr (funcall aff (list cand)))))))) + (lambda (str) + (orderless--match-p pred regexp (funcall fun str))))) + +;;; Highlighting matches + +(defun orderless--highlight (regexps ignore-case string) + "Destructively propertize STRING to highlight a match of each of the REGEXPS. +The search is case insensitive if IGNORE-CASE is non-nil." + (cl-loop with case-fold-search = ignore-case + with n = (length orderless-match-faces) + for regexp in regexps and i from 0 + when (string-match regexp string) do + (cl-loop + for (x y) on (let ((m (match-data))) (or (cddr m) m)) by #'cddr + when x do + (add-face-text-property + x y + (aref orderless-match-faces (mod i n)) + nil string))) + string) + +(defun orderless-highlight-matches (regexps strings) + "Highlight a match of each of the REGEXPS in each of the STRINGS. +Warning: only use this if you know all REGEXPs match all STRINGS! +For the user's convenience, if REGEXPS is a string, it is +converted to a list of regexps according to the value of +`orderless-matching-styles'." + (when (stringp regexps) + (setq regexps (cdr (orderless-compile regexps)))) + (cl-loop with ignore-case = (orderless--ignore-case-p regexps) + for str in strings + collect (orderless--highlight regexps ignore-case (substring str)))) + +;;; Compiling patterns to lists of regexps + +(defun orderless-escapable-split-on-space (string) + "Split STRING on spaces, which can be escaped with backslash." + (mapcar + (lambda (piece) (replace-regexp-in-string (string 0) " " piece)) + (split-string (replace-regexp-in-string + "\\\\\\\\\\|\\\\ " + (lambda (x) (if (equal x "\\ ") (string 0) x)) + string 'fixedcase 'literal) + " +" t))) + +(define-obsolete-function-alias 'orderless-dispatch 'orderless--dispatch "1.0") +(defun orderless--dispatch (dispatchers default string index total) + "Run DISPATCHERS to compute matching styles for STRING. + +A style dispatcher is a function that takes a STRING, component +INDEX and the TOTAL number of components. It should either +return (a) nil to indicate the dispatcher will not handle the +string, (b) a new string to replace the current string and +continue dispatch, or (c) the matching styles to use and, if +needed, a new string to use in place of the current one (for +example, a dispatcher can decide which style to use based on a +suffix of the string and then it must also return the component +stripped of the suffix). + +More precisely, the return value of a style dispatcher can be of +one of the following forms: + +- nil (to continue dispatching) + +- a string (to replace the component and continue dispatching), + +- a matching style or non-empty list of matching styles to + return, + +- a `cons' whose `car' is either as in the previous case or + nil (to request returning the DEFAULT matching styles), and + whose `cdr' is a string (to replace the current one). + +This function tries all DISPATCHERS in sequence until one returns +a list of styles. When that happens it returns a `cons' of the +list of styles and the possibly updated STRING. If none of the +DISPATCHERS returns a list of styles, the return value will use +DEFAULT as the list of styles." + (cl-loop for dispatcher in dispatchers + for result = (funcall dispatcher string index total) + if (stringp result) + do (setq string result result nil) + else if (and (consp result) (null (car result))) + do (setf (car result) default) + else if (and (consp result) (stringp (cdr result))) + do (setq string (cdr result) result (car result)) + when result return (cons result string) + finally (return (cons default string)))) + +(defun orderless--compile-component (component index total styles dispatchers) + "Compile COMPONENT at INDEX of TOTAL components with STYLES and DISPATCHERS." + (cl-loop + with pred = nil + with (newsty . newcomp) = (orderless--dispatch dispatchers styles + component index total) + for style in (if (functionp newsty) (list newsty) newsty) + for res = (condition-case nil + (funcall style newcomp) + (wrong-number-of-arguments + (when-let ((res (orderless--compile-component + newcomp index total styles dispatchers))) + (funcall style (car res) (cdr res))))) + if (functionp res) do (cl-callf orderless--predicate-and pred res) + else if res collect (if (stringp res) `(regexp ,res) res) into regexps + finally return + (when (or pred regexps) + (cons pred (and regexps (rx-to-string `(or ,@(delete-dups regexps)) t)))))) + +(defun orderless-compile (pattern &optional styles dispatchers) + "Build regexps to match the components of PATTERN. +Split PATTERN on `orderless-component-separator' and compute +matching styles for each component. For each component the style +DISPATCHERS are run to determine the matching styles to be used; +they are called with arguments the component, the 0-based index +of the component and the total number of components. If the +DISPATCHERS decline to handle the component, then the list of +matching STYLES is used. See `orderless--dispatch' for details +on dispatchers. + +The STYLES default to `orderless-matching-styles', and the +DISPATCHERS default to `orderless-dipatchers'. Since nil gets +you the default, if you want no dispatchers to be run, use +\\='(ignore) as the value of DISPATCHERS. + +The return value is a pair of a predicate function and a list of +regexps. The predicate function can also be nil. It takes a +string as argument." + (unless styles (setq styles orderless-matching-styles)) + (unless dispatchers (setq dispatchers orderless-style-dispatchers)) + (cl-loop + with predicate = nil + with components = (if (functionp orderless-component-separator) + (funcall orderless-component-separator pattern) + (split-string pattern orderless-component-separator t)) + with total = (length components) + for comp in components and index from 0 + for (pred . regexp) = (orderless--compile-component + comp index total styles dispatchers) + when regexp collect regexp into regexps + when pred do (cl-callf orderless--predicate-and predicate pred) + finally return (cons predicate regexps))) + +(defun orderless-pattern-compiler (pattern &optional styles dispatchers) + "Obsolete function, use `orderless-compile' instead. +See `orderless-compile' for the arguments PATTERN, STYLES and DISPATCHERS." + (cdr (orderless-compile pattern styles dispatchers))) +(make-obsolete 'orderless-pattern-compiler 'orderless-compile "1.0") + +;;; Completion style implementation + +(defun orderless--predicate-normalized-and (p q) + "Combine two predicate functions P and Q with `and'. +The first function P is a completion predicate which can receive +up to two arguments. The second function Q always receives a +normalized string as argument." + (cond + ((and p q) + (lambda (k &rest v) ;; v for hash table + (when (if v (funcall p k (car v)) (funcall p k)) + (setq k (if (consp k) (car k) k)) ;; alist + (funcall q (if (symbolp k) (symbol-name k) k))))) + (q + (lambda (k &optional _) ;; _ for hash table + (setq k (if (consp k) (car k) k)) ;; alist + (funcall q (if (symbolp k) (symbol-name k) k)))) + (p))) + +(defun orderless--predicate-and (p q) + "Combine two predicate functions P and Q with `and'." + (or (and p q (lambda (x) (and (funcall p x) (funcall q x)))) p q)) + +(defun orderless--compile (string table pred) + "Compile STRING to a prefix and a list of regular expressions. +The predicate PRED is used to constrain the entries in TABLE." + (pcase-let* ((limit (car (completion-boundaries string table pred ""))) + (prefix (substring string 0 limit)) + (pattern (substring string limit)) + (`(,fun . ,regexps) (orderless-compile pattern))) + (list prefix regexps (orderless--ignore-case-p regexps) + (orderless--predicate-normalized-and pred fun)))) + +;; Thanks to @jakanakaevangeli for writing a version of this function: +;; https://github.com/oantolin/orderless/issues/79#issuecomment-916073526 +(defun orderless--literal-prefix-p (regexp) + "Determine if REGEXP is a quoted regexp anchored at the beginning. +If REGEXP is of the form \"\\`q\" for q = (regexp-quote u), +then return (cons REGEXP u); else return nil." + (when (and (string-prefix-p "\\`" regexp) + (not (string-match-p "[$*+.?[\\^]" + (replace-regexp-in-string + "\\\\[$*+.?[\\^]" "" regexp + 'fixedcase 'literal nil 2)))) + (cons regexp + (replace-regexp-in-string "\\\\\\([$*+.?[\\^]\\)" "\\1" + regexp 'fixedcase nil nil 2)))) + +(defun orderless--ignore-case-p (regexps) + "Return non-nil if case should be ignored for REGEXPS." + (if orderless-smart-case + (cl-loop for regexp in regexps + always (isearch-no-upper-case-p regexp t)) + completion-ignore-case)) + +(defun orderless--filter (prefix regexps ignore-case table pred) + "Filter TABLE by PREFIX, REGEXPS and PRED. +The matching should be case-insensitive if IGNORE-CASE is non-nil." + ;; If there is a regexp of the form \`quoted-regexp then + ;; remove the first such and add the unquoted form to the prefix. + (pcase (cl-loop for r in regexps + thereis (orderless--literal-prefix-p r)) + (`(,regexp . ,literal) + (setq prefix (concat prefix literal) + regexps (remove regexp regexps)))) + (let ((completion-regexp-list regexps) + (completion-ignore-case ignore-case)) + (all-completions prefix table pred))) + +(defun orderless-filter (string table &optional pred) + "Split STRING into components and find entries TABLE matching all. +The predicate PRED is used to constrain the entries in TABLE." + (pcase-let ((`(,prefix ,regexps ,ignore-case ,pred) + (orderless--compile string table pred))) + (orderless--filter prefix regexps ignore-case table pred))) + +;;;###autoload +(defun orderless-all-completions (string table pred _point) + "Split STRING into components and find entries TABLE matching all. +The predicate PRED is used to constrain the entries in TABLE. The +matching portions of each candidate are highlighted. +This function is part of the `orderless' completion style." + (defvar completion-lazy-hilit-fn) + (pcase-let ((`(,prefix ,regexps ,ignore-case ,pred) + (orderless--compile string table pred))) + (when-let ((completions (orderless--filter prefix regexps ignore-case table pred))) + (if (bound-and-true-p completion-lazy-hilit) + (setq completion-lazy-hilit-fn + (apply-partially #'orderless--highlight regexps ignore-case)) + (cl-loop for str in-ref completions do + (setf str (orderless--highlight regexps ignore-case (substring str))))) + (nconc completions (length prefix))))) + +;;;###autoload +(defun orderless-try-completion (string table pred point) + "Complete STRING to unique matching entry in TABLE. +This uses `orderless-all-completions' to find matches for STRING +in TABLE among entries satisfying PRED. If there is only one +match, it completes to that match. If there are no matches, it +returns nil. In any other case it \"completes\" STRING to +itself, without moving POINT. +This function is part of the `orderless' completion style." + (catch 'orderless--many + (pcase-let ((`(,prefix ,regexps ,ignore-case ,pred) + (orderless--compile string table pred)) + (one nil)) + ;; Abuse all-completions/orderless--filter as a fast search loop. + ;; Should be almost allocation-free since our "predicate" is not + ;; called more than two times. + (orderless--filter + prefix regexps ignore-case table + (orderless--predicate-normalized-and + pred + (lambda (arg) + ;; Check if there is more than a single match (= many). + (when (and one (not (equal one arg))) + (throw 'orderless--many (cons string point))) + (setq one arg) + t))) + (when one + ;; Prepend prefix if the candidate does not already have the same + ;; prefix. This workaround is needed since the predicate may either + ;; receive an unprefixed or a prefixed candidate as argument. Most + ;; completion tables consistently call the predicate with unprefixed + ;; candidates, for example `completion-file-name-table'. In contrast, + ;; `completion-table-with-context' calls the predicate with prefixed + ;; candidates. This could be an unintended bug or oversight in + ;; `completion-table-with-context'. + (unless (or (equal prefix "") + (and (string-prefix-p prefix one) + (test-completion one table pred))) + (setq one (concat prefix one))) + (or (equal string one) ;; Return t for unique exact match + (cons one (length one))))))) + +;;;###autoload +(add-to-list 'completion-styles-alist + '(orderless + orderless-try-completion orderless-all-completions + "Completion of multiple components, in any order.")) + +(defmacro orderless-define-completion-style + (name &optional docstring &rest configuration) + "Define an orderless completion style with given CONFIGURATION. +The CONFIGURATION should be a list of bindings that you could use +with `let' to configure orderless. You can include bindings for +`orderless-matching-styles' and `orderless-style-dispatchers', +for example. + +The completion style consists of two functions that this macro +defines for you, NAME-try-completion and NAME-all-completions. +This macro registers those in `completion-styles-alist' as +forming the completion style NAME. + +The optional DOCSTRING argument is used as the documentation +string for the completion style." + (declare (doc-string 2) (indent 1)) + (unless (stringp docstring) + (push docstring configuration) + (setq docstring nil)) + (let* ((fn-name (lambda (string) (intern (concat (symbol-name name) string)))) + (try-completion (funcall fn-name "-try-completion")) + (all-completions (funcall fn-name "-all-completions")) + (doc-fmt "`%s' function for the %s style. +This function delegates to `orderless-%s'. +The orderless configuration is locally modified +specifically for the %s style.") + (fn-doc (lambda (fn) (format doc-fmt fn name fn name name)))) + `(progn + (defun ,try-completion (string table pred point) + ,(funcall fn-doc "try-completion") + (let ,configuration + (orderless-try-completion string table pred point))) + (defun ,all-completions (string table pred point) + ,(funcall fn-doc "all-completions") + (let ,configuration + (orderless-all-completions string table pred point))) + (add-to-list 'completion-styles-alist + '(,name ,try-completion ,all-completions ,docstring))))) + +;;; Ivy integration + +;;;###autoload +(defun orderless-ivy-re-builder (str) + "Convert STR into regexps for use with ivy. +This function is for integration of orderless with ivy, use it as +a value in `ivy-re-builders-alist'." + (or (mapcar (lambda (x) (cons x t)) (cdr (orderless-compile str))) "")) + +(defvar ivy-regex) +(defun orderless-ivy-highlight (str) + "Highlight a match in STR of each regexp in `ivy-regex'. +This function is for integration of orderless with ivy." + (orderless--highlight (mapcar #'car ivy-regex) t str) str) + +(provide 'orderless) +;;; orderless.el ends here diff --git a/emacs/elpa/orderless-20240606.1026/orderless.elc b/emacs/elpa/orderless-20240606.1026/orderless.elc Binary files differ. diff --git a/emacs/elpa/orderless-20240401.959/orderless.info b/emacs/elpa/orderless-20240606.1026/orderless.info diff --git a/emacs/elpa/transient-20240525.1118/transient-pkg.el b/emacs/elpa/transient-20240525.1118/transient-pkg.el @@ -1,16 +0,0 @@ -(define-package "transient" "20240525.1118" "Transient commands" - '((emacs "26.1") - (compat "29.1.4.4") - (seq "2.24")) - :commit "99a68578df4d938598d0fcbb8401e2fe35be6132" :authors - '(("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev")) - :maintainers - '(("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev")) - :maintainer - '("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev") - :keywords - '("extensions") - :url "https://github.com/magit/transient") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/transient-20240525.1118/transient.el b/emacs/elpa/transient-20240525.1118/transient.el @@ -1,4508 +0,0 @@ -;;; transient.el --- Transient commands -*- lexical-binding:t -*- - -;; Copyright (C) 2018-2024 Free Software Foundation, Inc. - -;; Author: Jonas Bernoulli <emacs.transient@jonas.bernoulli.dev> -;; Homepage: https://github.com/magit/transient -;; Keywords: extensions - -;; Package-Version: 0.6.0 -;; Package-Requires: ((emacs "26.1") (compat "29.1.4.4") (seq "2.24")) - -;; SPDX-License-Identifier: GPL-3.0-or-later - -;; This file is part of GNU Emacs. - -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published -;; by the Free Software Foundation, either version 3 of the License, -;; or (at your option) any later version. -;; -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see <https://www.gnu.org/licenses/>. - -;;; Commentary: - -;; Transient is the library used to implement the keyboard-driven menus -;; in Magit. It is distributed as a separate package, so that it can be -;; used to implement similar menus in other packages. - -;;; Code: - -(require 'cl-lib) -(require 'compat) -(require 'eieio) -(require 'edmacro) -(require 'format-spec) - -(eval-and-compile - (when (and (featurep' seq) - (not (fboundp 'seq-keep))) - (unload-feature 'seq 'force))) -(require 'seq) -(unless (fboundp 'seq-keep) - (display-warning 'transient (substitute-command-keys "\ -Transient requires `seq' >= 2.24, -but due to bad defaults, Emacs' package manager, refuses to -upgrade this and other built-in packages to higher releases -from GNU Elpa, when a package specifies that this is needed. - -To fix this, you have to add this to your init file: - - (setq package-install-upgrade-built-in t) - -Then evaluate that expression by placing the cursor after it -and typing \\[eval-last-sexp]. - -Once you have done that, you have to explicitly upgrade `seq': - - \\[package-upgrade] seq \\`RET' - -Then you also must make sure the updated version is loaded, -by evaluating this form: - - (progn (unload-feature 'seq t) (require 'seq)) - -Until you do this, you will get random errors about `seq-keep' -being undefined while using Transient. - -If you don't use the `package' package manager but still get -this warning, then your chosen package manager likely has a -similar defect.") :emergency)) - -(eval-when-compile (require 'subr-x)) - -(declare-function info "info" (&optional file-or-node buffer)) -(declare-function Man-find-section "man" (section)) -(declare-function Man-next-section "man" (n)) -(declare-function Man-getpage-in-background "man" (topic)) - -(defvar Man-notify-method) -(defvar pp-default-function) ; since Emacs 29.1 - -(defmacro static-if (condition then-form &rest else-forms) - "A conditional compilation macro. -Evaluate CONDITION at macro-expansion time. If it is non-nil, -expand the macro to THEN-FORM. Otherwise expand it to ELSE-FORMS -enclosed in a `progn' form. ELSE-FORMS may be empty." - (declare (indent 2) - (debug (sexp sexp &rest sexp))) - (if (eval condition lexical-binding) - then-form - (cons 'progn else-forms))) - -(defmacro transient--with-emergency-exit (id &rest body) - (declare (indent defun)) - (unless (keywordp id) - (setq body (cons id body)) - (setq id nil)) - `(condition-case err - (let ((debugger #'transient--exit-and-debug)) - ,(macroexp-progn body)) - ((debug error) - (transient--emergency-exit ,id) - (signal (car err) (cdr err))))) - -(defun transient--exit-and-debug (&rest args) - (transient--emergency-exit :debugger) - (apply #'debug args)) - -;;; Options - -(defgroup transient nil - "Transient commands." - :group 'extensions) - -(defcustom transient-show-popup t - "Whether to show the current transient in a popup buffer. -\\<transient-map> -- If t, then show the popup as soon as a transient prefix command - is invoked. - -- If nil, then do not show the popup unless the user explicitly - requests it, by pressing \\[transient-show] or a prefix key. - -- If a number, then delay displaying the popup and instead show - a brief one-line summary. If zero or negative, then suppress - even showing that summary and display the pressed key only. - - Show the popup when the user explicitly requests it by pressing - \\[transient-show] or a prefix key. Unless zero, then also show the popup - after that many seconds of inactivity (using the absolute value)." - :package-version '(transient . "0.1.0") - :group 'transient - :type '(choice (const :tag "instantly" t) - (const :tag "on demand" nil) - (const :tag "on demand (no summary)" 0) - (number :tag "after delay" 1))) - -(defcustom transient-enable-popup-navigation t - "Whether navigation commands are enabled in the transient popup. - -While a transient is active the transient popup buffer is not the -current buffer, making it necessary to use dedicated commands to -act on that buffer itself. If this is non-nil, then the following -bindings are available: - -\\<transient-popup-navigation-map>\ -- \\[transient-backward-button] moves the cursor to the previous suffix. -- \\[transient-forward-button] moves the cursor to the next suffix. -- \\[transient-push-button] invokes the suffix the cursor is on. -\\<transient-button-map>\ -- \\`<mouse-1>' and \\`<mouse-2>' invoke the clicked on suffix. -\\<transient-popup-navigation-map>\ -- \\[transient-isearch-backward]\ - and \\[transient-isearch-forward] start isearch in the popup buffer. - -\\`<mouse-1>' and \\`<mouse-2>' are bound in `transient-push-button'. -All other bindings are in `transient-popup-navigation-map'. - -By default \\`M-RET' is bound to `transient-push-button', instead of -\\`RET', because if a transient allows the invocation of non-suffixes -then it is likely that you would want \\`RET' to do what it would do -if no transient were active." - :package-version '(transient . "0.4.0") - :group 'transient - :type 'boolean) - -(defcustom transient-display-buffer-action - '(display-buffer-in-side-window - (side . bottom) - (dedicated . t) - (inhibit-same-window . t) - (window-parameters (no-other-window . t))) - "The action used to display the transient popup buffer. - -The transient popup buffer is displayed in a window using - - (display-buffer BUFFER transient-display-buffer-action) - -The value of this option has the form (FUNCTION . ALIST), -where FUNCTION is a function or a list of functions. Each such -function should accept two arguments: a buffer to display and an -alist of the same form as ALIST. See info node `(elisp)Choosing -Window' for details. - -The default is: - - (display-buffer-in-side-window - (side . bottom) - (dedicated . t) - (inhibit-same-window . t) - (window-parameters (no-other-window . t))) - -This displays the window at the bottom of the selected frame. -Another useful FUNCTION is `display-buffer-below-selected', which -is what `magit-popup' used by default. For more alternatives see -info node `(elisp)Display Action Functions' and info node -`(elisp)Buffer Display Action Alists'. - -Note that the buffer that was current before the transient buffer -is shown should remain the current buffer. Many suffix commands -act on the thing at point, if appropriate, and if the transient -buffer became the current buffer, then that would change what is -at point. To that effect `inhibit-same-window' ensures that the -selected window is not used to show the transient buffer. - -It may be possible to display the window in another frame, but -whether that works in practice depends on the window-manager. -If the window manager selects the new window (Emacs frame), -then that unfortunately changes which buffer is current. - -If you change the value of this option, then you might also -want to change the value of `transient-mode-line-format'." - :package-version '(transient . "0.3.0") - :group 'transient - :type '(cons (choice function (repeat :tag "Functions" function)) - alist)) - -(defcustom transient-mode-line-format 'line - "The mode-line format for the transient popup buffer. - -If nil, then the buffer has no mode-line. If the buffer is not -displayed right above the echo area, then this probably is not -a good value. - -If `line' (the default) or a natural number, then the buffer -has no mode-line, but a line is drawn is drawn in its place. -If a number is used, that specifies the thickness of the line. -On termcap frames we cannot draw lines, so there `line' and -numbers are synonyms for nil. - -The color of the line is used to indicate if non-suffixes are -allowed and whether they exit the transient. The foreground -color of `transient-key-noop' (if non-suffix are disallowed), -`transient-key-stay' (if allowed and transient stays active), or -`transient-key-exit' (if allowed and they exit the transient) is -used to draw the line. - -Otherwise this can be any mode-line format. -See `mode-line-format' for details." - :package-version '(transient . "0.2.0") - :group 'transient - :type '(choice (const :tag "hide mode-line" nil) - (const :tag "substitute thin line" line) - (number :tag "substitute line with thickness") - (const :tag "name of prefix command" - ("%e" mode-line-front-space - mode-line-buffer-identification)) - (sexp :tag "custom mode-line format"))) - -(defcustom transient-show-common-commands nil - "Whether to show common transient suffixes in the popup buffer. - -These commands are always shown after typing the prefix key -\"C-x\" when a transient command is active. To toggle the value -of this variable use \"C-x t\" when a transient is active." - :package-version '(transient . "0.1.0") - :group 'transient - :type 'boolean) - -(defcustom transient-read-with-initial-input nil - "Whether to use the last history element as initial minibuffer input." - :package-version '(transient . "0.2.0") - :group 'transient - :type 'boolean) - -(defcustom transient-highlight-mismatched-keys nil - "Whether to highlight keys that do not match their argument. - -This only affects infix arguments that represent command-line -arguments. When this option is non-nil, then the key binding -for infix argument are highlighted when only a long argument -\(e.g., \"--verbose\") is specified but no shorthand (e.g., \"-v\"). -In the rare case that a short-hand is specified but does not -match the key binding, then it is highlighted differently. - -The highlighting is done using `transient-mismatched-key' -and `transient-nonstandard-key'." - :package-version '(transient . "0.1.0") - :group 'transient - :type 'boolean) - -(defcustom transient-highlight-higher-levels nil - "Whether to highlight suffixes on higher levels. - -This is primarily intended for package authors. - -When non-nil then highlight the description of suffixes whose -level is above 4, the default of `transient-default-level'. -Assuming you have set that variable to 7, this highlights all -suffixes that won't be available to users without them making -the same customization." - :package-version '(transient . "0.3.6") - :group 'transient - :type 'boolean) - -(defcustom transient-substitute-key-function nil - "Function used to modify key bindings. - -This function is called with one argument, the prefix object, -and must return a key binding description, either the existing -key description it finds in the `key' slot, or a substitution. - -This is intended to let users replace certain prefix keys. It -could also be used to make other substitutions, but that is -discouraged. - -For example, \"=\" is hard to reach using my custom keyboard -layout, so I substitute \"(\" for that, which is easy to reach -using a layout optimized for Lisp. - - (setq transient-substitute-key-function - (lambda (obj) - (let ((key (oref obj key))) - (if (string-match \"\\\\`\\\\(=\\\\)[a-zA-Z]\" key) - (replace-match \"(\" t t key 1) - key)))))" - :package-version '(transient . "0.1.0") - :group 'transient - :type '(choice (const :tag "Transform no keys (nil)" nil) function)) - -(defcustom transient-semantic-coloring t - "Whether to use colors to indicate transient behavior. - -If non-nil, then the key binding of each suffix is colorized to -indicate whether it exits the transient state or not, and the -line that is drawn below the transient popup buffer is used to -indicate the behavior of non-suffix commands." - :package-version '(transient . "0.5.0") - :group 'transient - :type 'boolean) - -(defcustom transient-detect-key-conflicts nil - "Whether to detect key binding conflicts. - -Conflicts are detected when a transient prefix command is invoked -and results in an error, which prevents the transient from being -used." - :package-version '(transient . "0.1.0") - :group 'transient - :type 'boolean) - -(defcustom transient-align-variable-pitch nil - "Whether to align columns pixel-wise in the popup buffer. - -If this is non-nil, then columns are aligned pixel-wise to -support variable-pitch fonts. Keys are not aligned, so you -should use a fixed-pitch font for the `transient-key' face. -Other key faces inherit from that face unless a theme is -used that breaks that relationship. - -This option is intended for users who use a variable-pitch -font for the `default' face. - -Also see `transient-force-fixed-pitch'." - :package-version '(transient . "0.4.0") - :group 'transient - :type 'boolean) - -(defcustom transient-force-fixed-pitch nil - "Whether to force use of monospaced font in the popup buffer. - -Even if you use a proportional font for the `default' face, -you might still want to use a monospaced font in transient's -popup buffer. Setting this option to t causes `default' to -be remapped to `fixed-pitch' in that buffer. - -Also see `transient-align-variable-pitch'." - :package-version '(transient . "0.2.0") - :group 'transient - :type 'boolean) - -(defcustom transient-force-single-column nil - "Whether to force use of a single column to display suffixes. - -This might be useful for users with low vision who use large -text and might otherwise have to scroll in two dimensions." - :package-version '(transient . "0.3.6") - :group 'transient - :type 'boolean) - -(defcustom transient-hide-during-minibuffer-read nil - "Whether to hide the transient buffer while reading in the minibuffer." - :package-version '(transient . "0.4.0") - :group 'transient - :type 'boolean) - -(defconst transient--max-level 7) -(defconst transient--default-child-level 1) -(defconst transient--default-prefix-level 4) - -(defcustom transient-default-level transient--default-prefix-level - "Control what suffix levels are made available by default. - -Each suffix command is placed on a level and each prefix command -has a level, which controls which suffix commands are available. -Integers between 1 and 7 (inclusive) are valid levels. - -The levels of individual transients and/or their individual -suffixes can be changed individually, by invoking the prefix and -then pressing \"C-x l\". - -The default level for both transients and their suffixes is 4. -This option only controls the default for transients. The default -suffix level is always 4. The author of a transient should place -certain suffixes on a higher level if they expect that it won't be -of use to most users, and they should place very important suffixes -on a lower level so that they remain available even if the user -lowers the transient level. - -\(Magit currently places nearly all suffixes on level 4 and lower -levels are not used at all yet. So for the time being you should -not set a lower level here and using a higher level might not -give you as many additional suffixes as you hoped.)" - :package-version '(transient . "0.1.0") - :group 'transient - :type '(choice (const :tag "1 - fewest suffixes" 1) - (const 2) - (const 3) - (const :tag "4 - default" 4) - (const 5) - (const 6) - (const :tag "7 - most suffixes" 7))) - -(defcustom transient-levels-file - (locate-user-emacs-file "transient/levels.el") - "File used to save levels of transients and their suffixes." - :package-version '(transient . "0.1.0") - :group 'transient - :type 'file) - -(defcustom transient-values-file - (locate-user-emacs-file "transient/values.el") - "File used to save values of transients." - :package-version '(transient . "0.1.0") - :group 'transient - :type 'file) - -(defcustom transient-history-file - (locate-user-emacs-file "transient/history.el") - "File used to save history of transients and their infixes." - :package-version '(transient . "0.1.0") - :group 'transient - :type 'file) - -(defcustom transient-history-limit 10 - "Number of history elements to keep when saving to file." - :package-version '(transient . "0.1.0") - :group 'transient - :type 'integer) - -(defcustom transient-save-history t - "Whether to save history of transient commands when exiting Emacs." - :package-version '(transient . "0.1.0") - :group 'transient - :type 'boolean) - -;;; Faces - -(defgroup transient-faces nil - "Faces used by Transient." - :group 'transient) - -(defface transient-heading '((t :inherit font-lock-keyword-face)) - "Face used for headings." - :group 'transient-faces) - -(defface transient-argument '((t :inherit font-lock-string-face :weight bold)) - "Face used for enabled arguments." - :group 'transient-faces) - -(defface transient-inactive-argument '((t :inherit shadow)) - "Face used for inactive arguments." - :group 'transient-faces) - -(defface transient-value '((t :inherit font-lock-string-face :weight bold)) - "Face used for values." - :group 'transient-faces) - -(defface transient-inactive-value '((t :inherit shadow)) - "Face used for inactive values." - :group 'transient-faces) - -(defface transient-unreachable '((t :inherit shadow)) - "Face used for suffixes unreachable from the current prefix sequence." - :group 'transient-faces) - -(defface transient-inapt-suffix '((t :inherit shadow :italic t)) - "Face used for suffixes that are inapt at this time." - :group 'transient-faces) - -(defface transient-active-infix '((t :inherit highlight)) - "Face used for the infix for which the value is being read." - :group 'transient-faces) - -(defface transient-enabled-suffix - '((t :background "green" :foreground "black" :weight bold)) - "Face used for enabled levels while editing suffix levels. -See info node `(transient)Enabling and Disabling Suffixes'." - :group 'transient-faces) - -(defface transient-disabled-suffix - '((t :background "red" :foreground "black" :weight bold)) - "Face used for disabled levels while editing suffix levels. -See info node `(transient)Enabling and Disabling Suffixes'." - :group 'transient-faces) - -(defface transient-higher-level - `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) - :color ,(let ((color (face-attribute 'shadow :foreground nil t))) - (or (and (not (eq color 'unspecified)) color) - "grey60"))))) - "Face optionally used to highlight suffixes on higher levels. -Also see option `transient-highlight-higher-levels'." - :group 'transient-faces) - -(defface transient-delimiter '((t :inherit shadow)) - "Face used for delimiters and separators. -This includes the parentheses around values and the pipe -character used to separate possible values from each other." - :group 'transient-faces) - -(defface transient-key '((t :inherit font-lock-builtin-face)) - "Face used for keys." - :group 'transient-faces) - -(defface transient-key-stay - `((((class color) (background light)) - :inherit transient-key - :foreground "#22aa22") - (((class color) (background dark)) - :inherit transient-key - :foreground "#ddffdd")) - "Face used for keys of suffixes that don't exit transient state." - :group 'transient-faces) - -(defface transient-key-noop - `((((class color) (background light)) - :inherit transient-key - :foreground "grey80") - (((class color) (background dark)) - :inherit transient-key - :foreground "grey30")) - "Face used for keys of suffixes that currently cannot be invoked." - :group 'transient-faces) - -(defface transient-key-return - `((((class color) (background light)) - :inherit transient-key - :foreground "#aaaa11") - (((class color) (background dark)) - :inherit transient-key - :foreground "#ffffcc")) - "Face used for keys of suffixes that return to the parent transient." - :group 'transient-faces) - -(defface transient-key-exit - `((((class color) (background light)) - :inherit transient-key - :foreground "#aa2222") - (((class color) (background dark)) - :inherit transient-key - :foreground "#ffdddd")) - "Face used for keys of suffixes that exit transient state." - :group 'transient-faces) - -(defface transient-unreachable-key - '((t :inherit (shadow transient-key) :weight normal)) - "Face used for keys unreachable from the current prefix sequence." - :group 'transient-faces) - -(defface transient-nonstandard-key - `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) - :color "cyan"))) - "Face optionally used to highlight keys conflicting with short-argument. -Also see option `transient-highlight-mismatched-keys'." - :group 'transient-faces) - -(defface transient-mismatched-key - `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) - :color "magenta"))) - "Face optionally used to highlight keys without a short-argument. -Also see option `transient-highlight-mismatched-keys'." - :group 'transient-faces) - -;;; Persistence - -(defun transient--read-file-contents (file) - (with-demoted-errors "Transient error: %S" - (and (file-exists-p file) - (with-temp-buffer - (insert-file-contents file) - (read (current-buffer)))))) - -(defun transient--pp-to-file (list file) - (make-directory (file-name-directory file) t) - (setq list (cl-sort (copy-sequence list) #'string< :key #'car)) - (with-temp-file file - (let ((print-level nil) - (print-length nil) - (pp-default-function 'pp-28) - (fill-column 999)) - (pp list (current-buffer))))) - -(defvar transient-values - (transient--read-file-contents transient-values-file) - "Values of transient commands. -The value of this variable persists between Emacs sessions -and you usually should not change it manually.") - -(defun transient-save-values () - (transient--pp-to-file transient-values transient-values-file)) - -(defvar transient-levels - (transient--read-file-contents transient-levels-file) - "Levels of transient commands. -The value of this variable persists between Emacs sessions -and you usually should not change it manually.") - -(defun transient-save-levels () - (transient--pp-to-file transient-levels transient-levels-file)) - -(defvar transient-history - (transient--read-file-contents transient-history-file) - "History of transient commands and infix arguments. -The value of this variable persists between Emacs sessions -\(unless `transient-save-history' is nil) and you usually -should not change it manually.") - -(defun transient-save-history () - (setq transient-history - (cl-sort (mapcar (pcase-lambda (`(,key . ,val)) - (cons key (seq-take (delete-dups val) - transient-history-limit))) - transient-history) - #'string< :key #'car)) - (transient--pp-to-file transient-history transient-history-file)) - -(defun transient-maybe-save-history () - "Save the value of `transient-history'. -If `transient-save-history' is nil, then do nothing." - (when transient-save-history - (transient-save-history))) - -(unless noninteractive - (add-hook 'kill-emacs-hook #'transient-maybe-save-history)) - -;;; Classes -;;;; Prefix - -(defclass transient-prefix () - ((prototype :initarg :prototype) - (command :initarg :command) - (level :initarg :level) - (variable :initarg :variable :initform nil) - (init-value :initarg :init-value) - (value) (default-value :initarg :value) - (scope :initarg :scope :initform nil) - (history :initarg :history :initform nil) - (history-pos :initarg :history-pos :initform 0) - (history-key :initarg :history-key :initform nil) - (show-help :initarg :show-help :initform nil) - (info-manual :initarg :info-manual :initform nil) - (man-page :initarg :man-page :initform nil) - (transient-suffix :initarg :transient-suffix :initform nil) - (transient-non-suffix :initarg :transient-non-suffix :initform nil) - (transient-switch-frame :initarg :transient-switch-frame) - (refresh-suffixes :initarg :refresh-suffixes :initform nil) - (incompatible :initarg :incompatible :initform nil) - (suffix-description :initarg :suffix-description) - (variable-pitch :initarg :variable-pitch :initform nil) - (column-widths :initarg :column-widths :initform nil) - (unwind-suffix :documentation "Internal use." :initform nil)) - "Transient prefix command. - -Each transient prefix command consists of a command, which is -stored in a symbol's function slot and an object, which is -stored in the `transient--prefix' property of the same symbol. - -When a transient prefix command is invoked, then a clone of that -object is stored in the global variable `transient--prefix' and -the prototype is stored in the clone's `prototype' slot.") - -;;;; Suffix - -(defclass transient-child () - ((level - :initarg :level - :initform (symbol-value 'transient--default-child-level) - :documentation "Enable if level of prefix is equal or greater.") - (if - :initarg :if - :initform nil - :documentation "Enable if predicate returns non-nil.") - (if-not - :initarg :if-not - :initform nil - :documentation "Enable if predicate returns nil.") - (if-non-nil - :initarg :if-non-nil - :initform nil - :documentation "Enable if variable's value is non-nil.") - (if-nil - :initarg :if-nil - :initform nil - :documentation "Enable if variable's value is nil.") - (if-mode - :initarg :if-mode - :initform nil - :documentation "Enable if major-mode matches value.") - (if-not-mode - :initarg :if-not-mode - :initform nil - :documentation "Enable if major-mode does not match value.") - (if-derived - :initarg :if-derived - :initform nil - :documentation "Enable if major-mode derives from value.") - (if-not-derived - :initarg :if-not-derived - :initform nil - :documentation "Enable if major-mode does not derive from value.") - (inapt - :initform nil) - (inapt-face - :initarg :inapt-face - :initform 'transient-inapt-suffix) - (inapt-if - :initarg :inapt-if - :initform nil - :documentation "Inapt if predicate returns non-nil.") - (inapt-if-not - :initarg :inapt-if-not - :initform nil - :documentation "Inapt if predicate returns nil.") - (inapt-if-non-nil - :initarg :inapt-if-non-nil - :initform nil - :documentation "Inapt if variable's value is non-nil.") - (inapt-if-nil - :initarg :inapt-if-nil - :initform nil - :documentation "Inapt if variable's value is nil.") - (inapt-if-mode - :initarg :inapt-if-mode - :initform nil - :documentation "Inapt if major-mode matches value.") - (inapt-if-not-mode - :initarg :inapt-if-not-mode - :initform nil - :documentation "Inapt if major-mode does not match value.") - (inapt-if-derived - :initarg :inapt-if-derived - :initform nil - :documentation "Inapt if major-mode derives from value.") - (inapt-if-not-derived - :initarg :inapt-if-not-derived - :initform nil - :documentation "Inapt if major-mode does not derive from value.")) - "Abstract superclass for group and suffix classes. - -It is undefined what happens if more than one `if*' predicate -slot is non-nil." - :abstract t) - -(defclass transient-suffix (transient-child) - ((definition :allocation :class :initform nil) - (key :initarg :key) - (command :initarg :command) - (transient :initarg :transient) - (format :initarg :format :initform " %k %d") - (description :initarg :description :initform nil) - (face :initarg :face :initform nil) - (show-help :initarg :show-help :initform nil)) - "Superclass for suffix command.") - -(defclass transient-information (transient-suffix) - ((format :initform " %k %d") - (key :initform " ")) - "Display-only information, aligned with suffix keys. -Technically a suffix object with no associated command.") - -(defclass transient-information* (transient-information) - ((format :initform " %d")) - "Display-only information, aligned with suffix descriptions. -Technically a suffix object with no associated command.") - -(defclass transient-infix (transient-suffix) - ((transient :initform t) - (argument :initarg :argument) - (shortarg :initarg :shortarg) - (value :initform nil) - (init-value :initarg :init-value) - (unsavable :initarg :unsavable :initform nil) - (multi-value :initarg :multi-value :initform nil) - (always-read :initarg :always-read :initform nil) - (allow-empty :initarg :allow-empty :initform nil) - (history-key :initarg :history-key :initform nil) - (reader :initarg :reader :initform nil) - (prompt :initarg :prompt :initform nil) - (choices :initarg :choices :initform nil) - (format :initform " %k %d (%v)")) - "Transient infix command." - :abstract t) - -(defclass transient-argument (transient-infix) () - "Abstract superclass for infix arguments." - :abstract t) - -(defclass transient-switch (transient-argument) () - "Class used for command-line argument that can be turned on and off.") - -(defclass transient-option (transient-argument) () - "Class used for command-line argument that can take a value.") - -(defclass transient-variable (transient-infix) - ((variable :initarg :variable) - (format :initform " %k %d %v")) - "Abstract superclass for infix commands that set a variable." - :abstract t) - -(defclass transient-switches (transient-argument) - ((argument-format :initarg :argument-format) - (argument-regexp :initarg :argument-regexp)) - "Class used for sets of mutually exclusive command-line switches.") - -(defclass transient-files (transient-option) () - ((key :initform "--") - (argument :initform "--") - (multi-value :initform rest) - (reader :initform transient-read-files)) - "Class used for the \"--\" argument or similar. -All remaining arguments are treated as files. -They become the value of this argument.") - -;;;; Group - -(defclass transient-group (transient-child) - ((suffixes :initarg :suffixes :initform nil) - (hide :initarg :hide :initform nil) - (description :initarg :description :initform nil) - (pad-keys :initarg :pad-keys :initform nil) - (info-format :initarg :info-format :initform nil) - (setup-children :initarg :setup-children)) - "Abstract superclass of all group classes." - :abstract t) - -(defclass transient-column (transient-group) () - "Group class that displays each element on a separate line.") - -(defclass transient-row (transient-group) () - "Group class that displays all elements on a single line.") - -(defclass transient-columns (transient-group) () - "Group class that displays elements organized in columns. -Direct elements have to be groups whose elements have to be -commands or strings. Each subgroup represents a column. -This class takes care of inserting the subgroups' elements.") - -(defclass transient-subgroups (transient-group) () - "Group class that wraps other groups. - -Direct elements have to be groups whose elements have to be -commands or strings. This group inserts an empty line between -subgroups. The subgroups are responsible for displaying their -elements themselves.") - -;;; Define - -(defmacro transient-define-prefix (name arglist &rest args) - "Define NAME as a transient prefix command. - -ARGLIST are the arguments that command takes. -DOCSTRING is the documentation string and is optional. - -These arguments can optionally be followed by key-value pairs. -Each key has to be a keyword symbol, either `:class' or a keyword -argument supported by the constructor of that class. The -`transient-prefix' class is used if the class is not specified -explicitly. - -GROUPs add key bindings for infix and suffix commands and specify -how these bindings are presented in the popup buffer. At least -one GROUP has to be specified. See info node `(transient)Binding -Suffix and Infix Commands'. - -The BODY is optional. If it is omitted, then ARGLIST is also -ignored and the function definition becomes: - - (lambda () - (interactive) - (transient-setup \\='NAME)) - -If BODY is specified, then it must begin with an `interactive' -form that matches ARGLIST, and it must call `transient-setup'. -It may however call that function only when some condition is -satisfied; that is one of the reason why you might want to use -an explicit BODY. - -All transients have a (possibly nil) value, which is exported -when suffix commands are called, so that they can consume that -value. For some transients it might be necessary to have a sort -of secondary value, called a scope. Such a scope would usually -be set in the commands `interactive' form and has to be passed -to the setup function: - - (transient-setup \\='NAME nil nil :scope SCOPE) - -\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]... GROUP... [BODY...])" - (declare (debug ( &define name lambda-list - [&optional lambda-doc] - [&rest keywordp sexp] - [&rest vectorp] - [&optional ("interactive" interactive) def-body])) - (indent defun) - (doc-string 3)) - (pcase-let ((`(,class ,slots ,suffixes ,docstr ,body ,interactive-only) - (transient--expand-define-args args arglist))) - `(progn - (defalias ',name - ,(if body - `(lambda ,arglist ,@body) - `(lambda () - (interactive) - (transient-setup ',name)))) - (put ',name 'interactive-only ,interactive-only) - (put ',name 'function-documentation ,docstr) - (put ',name 'transient--prefix - (,(or class 'transient-prefix) :command ',name ,@slots)) - (put ',name 'transient--layout - (list ,@(cl-mapcan (lambda (s) (transient--parse-child name s)) - suffixes)))))) - -(defmacro transient-define-suffix (name arglist &rest args) - "Define NAME as a transient suffix command. - -ARGLIST are the arguments that the command takes. -DOCSTRING is the documentation string and is optional. - -These arguments can optionally be followed by key-value pairs. -Each key has to be a keyword symbol, either `:class' or a -keyword argument supported by the constructor of that class. -The `transient-suffix' class is used if the class is not -specified explicitly. - -The BODY must begin with an `interactive' form that matches -ARGLIST. The infix arguments are usually accessed by using -`transient-args' inside `interactive'. - -\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]... [BODY...])" - (declare (debug ( &define name lambda-list - [&optional lambda-doc] - [&rest keywordp sexp] - [&optional ("interactive" interactive) def-body])) - (indent defun) - (doc-string 3)) - (pcase-let ((`(,class ,slots ,_ ,docstr ,body ,interactive-only) - (transient--expand-define-args args arglist))) - `(progn - (defalias ',name - ,(if (and (not body) class (oref-default class definition)) - `(oref-default ',class definition) - `(lambda ,arglist ,@body))) - (put ',name 'interactive-only ,interactive-only) - (put ',name 'function-documentation ,docstr) - (put ',name 'transient--suffix - (,(or class 'transient-suffix) :command ',name ,@slots))))) - -(defmacro transient-define-infix (name arglist &rest args) - "Define NAME as a transient infix command. - -ARGLIST is always ignored and reserved for future use. -DOCSTRING is the documentation string and is optional. - -At least one key-value pair is required. All transient infix -commands are equal to each other (but not eq). It is meaning- -less to define an infix command, without providing at least one -keyword argument (usually `:argument' or `:variable', depending -on the class). The suffix class defaults to `transient-switch' -and can be set using the `:class' keyword. - -The function definitions is always: - - (lambda () - (interactive) - (let ((obj (transient-suffix-object))) - (transient-infix-set obj (transient-infix-read obj))) - (transient--show)) - -`transient-infix-read' and `transient-infix-set' are generic -functions. Different infix commands behave differently because -the concrete methods are different for different infix command -classes. In rare case the above command function might not be -suitable, even if you define your own infix command class. In -that case you have to use `transient-define-suffix' to define -the infix command and use t as the value of the `:transient' -keyword. - -\(fn NAME ARGLIST [DOCSTRING] KEYWORD VALUE [KEYWORD VALUE]...)" - (declare (debug ( &define name lambda-list - [&optional lambda-doc] - keywordp sexp - [&rest keywordp sexp])) - (indent defun) - (doc-string 3)) - (pcase-let ((`(,class ,slots ,_ ,docstr ,_ ,interactive-only) - (transient--expand-define-args args arglist t))) - `(progn - (defalias ',name #'transient--default-infix-command) - (put ',name 'interactive-only ,interactive-only) - (put ',name 'completion-predicate #'transient--suffix-only) - (put ',name 'function-documentation ,docstr) - (put ',name 'transient--suffix - (,(or class 'transient-switch) :command ',name ,@slots))))) - -(defalias 'transient-define-argument #'transient-define-infix - "Define NAME as a transient infix command. - -Only use this alias to define an infix command that actually -sets an infix argument. To define a infix command that, for -example, sets a variable, use `transient-define-infix' instead. - -\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]...)") - -(defun transient--default-infix-command () - ;; Most infix commands are but an alias for this command. - "Cannot show any documentation for this transient infix command. - -When you request help for an infix command using `transient-help', that -usually shows the respective man-page and tries to jump to the location -where the respective argument is being described. - -If no man-page is specified for the containing transient menu, then the -docstring is displayed instead, if any. - -If the infix command doesn't have a docstring, as is the case here, then -this docstring is displayed instead, because technically infix commands -are aliases for `transient--default-infix-command'. - -`describe-function' also shows the docstring of the infix command, -falling back to that of the same aliased command." - (interactive) - (let ((obj (transient-suffix-object))) - (transient-infix-set obj (transient-infix-read obj))) - (transient--show)) -(put 'transient--default-infix-command 'interactive-only t) -(put 'transient--default-infix-command 'completion-predicate - #'transient--suffix-only) - -(defun transient--find-function-advised-original (fn func) - "Return nil instead of `transient--default-infix-command'. -When using `find-function' to jump to the definition of a transient -infix command/argument, then we want to actually jump to that, not to -the definition of `transient--default-infix-command', which all infix -commands are aliases for." - (let ((val (funcall fn func))) - (and val (not (eq val 'transient--default-infix-command)) val))) -(advice-add 'find-function-advised-original :around - #'transient--find-function-advised-original) - -(eval-and-compile - (defun transient--expand-define-args (args &optional arglist nobody) - (unless (listp arglist) - (error "Mandatory ARGLIST is missing")) - (let (class keys suffixes docstr declare (interactive-only t)) - (when (stringp (car args)) - (setq docstr (pop args))) - (while (keywordp (car args)) - (let ((k (pop args)) - (v (pop args))) - (if (eq k :class) - (setq class v) - (push k keys) - (push v keys)))) - (while (let ((arg (car args))) - (or (vectorp arg) - (and arg (symbolp arg)))) - (push (pop args) suffixes)) - (when (eq (car-safe (car args)) 'declare) - (setq declare (car args)) - (setq args (cdr args)) - (when-let ((int (assq 'interactive-only declare))) - (setq interactive-only (cadr int)) - (delq int declare)) - (unless (cdr declare) - (setq declare nil))) - (cond - ((not args)) - (nobody - (error "transient-define-infix: No function body allowed")) - ((not (eq (car-safe (nth (if declare 1 0) args)) 'interactive)) - (error "transient-define-*: Interactive form missing"))) - (list (if (eq (car-safe class) 'quote) - (cadr class) - class) - (nreverse keys) - (nreverse suffixes) - docstr - (if declare (cons declare args) args) - interactive-only)))) - -(defun transient--parse-child (prefix spec) - (cl-typecase spec - (null (error "Invalid transient--parse-child spec: %s" spec)) - (symbol (let ((value (symbol-value spec))) - (if (and (listp value) - (or (listp (car value)) - (vectorp (car value)))) - (cl-mapcan (lambda (s) (transient--parse-child prefix s)) value) - (transient--parse-child prefix value)))) - (vector (and-let* ((c (transient--parse-group prefix spec))) (list c))) - (list (and-let* ((c (transient--parse-suffix prefix spec))) (list c))) - (string (list spec)) - (t (error "Invalid transient--parse-child spec: %s" spec)))) - -(defun transient--parse-group (prefix spec) - (setq spec (append spec nil)) - (cl-symbol-macrolet - ((car (car spec)) - (pop (pop spec))) - (let (level class args) - (when (integerp car) - (setq level pop)) - (when (stringp car) - (setq args (plist-put args :description pop))) - (while (keywordp car) - (let ((key pop) - (val pop)) - (cond ((eq key :class) - (setq class (macroexp-quote val))) - ((or (symbolp val) - (and (listp val) (not (eq (car val) 'lambda)))) - (setq args (plist-put args key (macroexp-quote val)))) - ((setq args (plist-put args key val)))))) - (unless (or spec class (not (plist-get args :setup-children))) - (message "WARNING: %s: When %s is used, %s must also be specified" - 'transient-define-prefix :setup-children :class)) - (list 'vector - (or level transient--default-child-level) - (cond (class) - ((or (vectorp car) - (and car (symbolp car))) - (quote 'transient-columns)) - ((quote 'transient-column))) - (and args (cons 'list args)) - (cons 'list - (cl-mapcan (lambda (s) (transient--parse-child prefix s)) - spec)))))) - -(defun transient--parse-suffix (prefix spec) - (let (level class args) - (cl-symbol-macrolet - ((car (car spec)) - (pop (pop spec))) - (when (integerp car) - (setq level pop)) - (when (or (stringp car) - (vectorp car)) - (setq args (plist-put args :key pop))) - (cond - ((or (stringp car) - (and (eq (car-safe car) 'lambda) - (not (commandp car)))) - (setq args (plist-put args :description pop))) - ((and (symbolp car) - (not (keywordp car)) - (not (commandp car)) - (commandp (cadr spec))) - (setq args (plist-put args :description (macroexp-quote pop))))) - (cond - ((memq car '(:info :info*))) - ((keywordp car) - (error "Need command, `:info' or `:info*', got `%s'" car)) - ((symbolp car) - (setq args (plist-put args :command (macroexp-quote pop)))) - ((and (commandp car) - (not (stringp car))) - (let ((cmd pop) - (sym (intern - (format "transient:%s:%s" - prefix - (let ((desc (plist-get args :description))) - (if (and (stringp desc) - (length< desc 16)) - desc - (plist-get args :key))))))) - (setq args (plist-put - args :command - `(prog1 ',sym - (put ',sym 'interactive-only t) - (put ',sym 'completion-predicate #'transient--suffix-only) - (defalias ',sym - ,(if (eq (car-safe cmd) 'lambda) - cmd - (macroexp-quote cmd)))))))) - ((or (stringp car) - (and car (listp car))) - (let ((arg pop) - (sym nil)) - (cl-typecase arg - (list - (setq args (plist-put args :shortarg (car arg))) - (setq args (plist-put args :argument (cadr arg))) - (setq arg (cadr arg))) - (string - (when-let ((shortarg (transient--derive-shortarg arg))) - (setq args (plist-put args :shortarg shortarg))) - (setq args (plist-put args :argument arg)))) - (setq sym (intern (format "transient:%s:%s" prefix arg))) - (setq args (plist-put - args :command - `(prog1 ',sym - (put ',sym 'interactive-only t) - (put ',sym 'completion-predicate #'transient--suffix-only) - (defalias ',sym #'transient--default-infix-command)))) - (cond ((and car (not (keywordp car))) - (setq class 'transient-option) - (setq args (plist-put args :reader (macroexp-quote pop)))) - ((not (string-suffix-p "=" arg)) - (setq class 'transient-switch)) - (t - (setq class 'transient-option))))) - (t - (error "Needed command or argument, got %S" car))) - (while (keywordp car) - (let ((key pop) - (val pop)) - (cond ((eq key :class) (setq class val)) - ((eq key :level) (setq level val)) - ((eq key :info) - (setq class 'transient-information) - (setq args (plist-put args :description val))) - ((eq key :info*) - (setq class 'transient-information*) - (setq args (plist-put args :description val))) - ((eq (car-safe val) '\,) - (setq args (plist-put args key (cadr val)))) - ((or (symbolp val) - (and (listp val) (not (eq (car val) 'lambda)))) - (setq args (plist-put args key (macroexp-quote val)))) - ((setq args (plist-put args key val))))))) - (unless (plist-get args :key) - (when-let ((shortarg (plist-get args :shortarg))) - (setq args (plist-put args :key shortarg)))) - (list 'list - (or level transient--default-child-level) - (macroexp-quote (or class 'transient-suffix)) - (cons 'list args)))) - -(defun transient--derive-shortarg (arg) - (save-match-data - (and (string-match "\\`\\(-[a-zA-Z]\\)\\(\\'\\|=\\)" arg) - (match-string 1 arg)))) - -(defun transient-command-completion-not-suffix-only-p (symbol _buffer) - "Say whether SYMBOL should be offered as a completion. -If the value of SYMBOL's `completion-predicate' property is -`transient--suffix-only', then return nil, otherwise return t. -This is the case when a command should only ever be used as a -suffix of a transient prefix command (as opposed to bindings -in regular keymaps or by using `execute-extended-command')." - (not (eq (get symbol 'completion-predicate) 'transient--suffix-only))) - -(defalias 'transient--suffix-only #'ignore - "Ignore ARGUMENTS, do nothing, and return nil. -Also see `transient-command-completion-not-suffix-only-p'. -Only use this alias as the value of the `completion-predicate' -symbol property.") - -(when (and (boundp 'read-extended-command-predicate) ; since Emacs 28.1 - (not read-extended-command-predicate)) - (setq read-extended-command-predicate - #'transient-command-completion-not-suffix-only-p)) - -(defun transient-parse-suffix (prefix suffix) - "Parse SUFFIX, to be added to PREFIX. -PREFIX is a prefix command, a symbol. -SUFFIX is a suffix command or a group specification (of - the same forms as expected by `transient-define-prefix'). -Intended for use in a group's `:setup-children' function." - (cl-assert (and prefix (symbolp prefix))) - (eval (car (transient--parse-child prefix suffix)) t)) - -(defun transient-parse-suffixes (prefix suffixes) - "Parse SUFFIXES, to be added to PREFIX. -PREFIX is a prefix command, a symbol. -SUFFIXES is a list of suffix command or a group specification - (of the same forms as expected by `transient-define-prefix'). -Intended for use in a group's `:setup-children' function." - (cl-assert (and prefix (symbolp prefix))) - (mapcar (apply-partially #'transient-parse-suffix prefix) suffixes)) - -;;; Edit - -(defun transient--insert-suffix (prefix loc suffix action &optional keep-other) - (let* ((suf (cl-etypecase suffix - (vector (transient--parse-group prefix suffix)) - (list (transient--parse-suffix prefix suffix)) - (string suffix))) - (mem (transient--layout-member loc prefix)) - (elt (car mem))) - (setq suf (eval suf t)) - (cond - ((not mem) - (message "Cannot insert %S into %s; %s not found" - suffix prefix loc)) - ((or (and (vectorp suffix) (not (vectorp elt))) - (and (listp suffix) (vectorp elt)) - (and (stringp suffix) (vectorp elt))) - (message "Cannot place %S into %s at %s; %s" - suffix prefix loc - "suffixes and groups cannot be siblings")) - (t - (when-let* ((bindingp (listp suf)) - (key (transient--spec-key suf)) - (conflict (car (transient--layout-member key prefix))) - (conflictp - (and (not (and (eq action 'replace) - (eq conflict elt))) - (or (not keep-other) - (eq (plist-get (nth 2 suf) :command) - (plist-get (nth 2 conflict) :command))) - (equal (transient--suffix-predicate suf) - (transient--suffix-predicate conflict))))) - (transient-remove-suffix prefix key)) - (pcase-exhaustive action - ('insert (setcdr mem (cons elt (cdr mem))) - (setcar mem suf)) - ('append (setcdr mem (cons suf (cdr mem)))) - ('replace (setcar mem suf))))))) - -;;;###autoload -(defun transient-insert-suffix (prefix loc suffix &optional keep-other) - "Insert a SUFFIX into PREFIX before LOC. -PREFIX is a prefix command, a symbol. -SUFFIX is a suffix command or a group specification (of - the same forms as expected by `transient-define-prefix'). -LOC is a command, a key vector, a key description (a string - as returned by `key-description'), or a coordination list - (whose last element may also be a command or key). -Remove a conflicting binding unless optional KEEP-OTHER is - non-nil. -See info node `(transient)Modifying Existing Transients'." - (declare (indent defun)) - (transient--insert-suffix prefix loc suffix 'insert keep-other)) - -;;;###autoload -(defun transient-append-suffix (prefix loc suffix &optional keep-other) - "Insert a SUFFIX into PREFIX after LOC. -PREFIX is a prefix command, a symbol. -SUFFIX is a suffix command or a group specification (of - the same forms as expected by `transient-define-prefix'). -LOC is a command, a key vector, a key description (a string - as returned by `key-description'), or a coordination list - (whose last element may also be a command or key). -Remove a conflicting binding unless optional KEEP-OTHER is - non-nil. -See info node `(transient)Modifying Existing Transients'." - (declare (indent defun)) - (transient--insert-suffix prefix loc suffix 'append keep-other)) - -;;;###autoload -(defun transient-replace-suffix (prefix loc suffix) - "Replace the suffix at LOC in PREFIX with SUFFIX. -PREFIX is a prefix command, a symbol. -SUFFIX is a suffix command or a group specification (of - the same forms as expected by `transient-define-prefix'). -LOC is a command, a key vector, a key description (a string - as returned by `key-description'), or a coordination list - (whose last element may also be a command or key). -See info node `(transient)Modifying Existing Transients'." - (declare (indent defun)) - (transient--insert-suffix prefix loc suffix 'replace)) - -;;;###autoload -(defun transient-remove-suffix (prefix loc) - "Remove the suffix or group at LOC in PREFIX. -PREFIX is a prefix command, a symbol. -LOC is a command, a key vector, a key description (a string - as returned by `key-description'), or a coordination list - (whose last element may also be a command or key). -See info node `(transient)Modifying Existing Transients'." - (declare (indent defun)) - (transient--layout-member loc prefix 'remove)) - -(defun transient-get-suffix (prefix loc) - "Return the suffix or group at LOC in PREFIX. -PREFIX is a prefix command, a symbol. -LOC is a command, a key vector, a key description (a string - as returned by `key-description'), or a coordination list - (whose last element may also be a command or key). -See info node `(transient)Modifying Existing Transients'." - (if-let ((mem (transient--layout-member loc prefix))) - (car mem) - (error "%s not found in %s" loc prefix))) - -(defun transient-suffix-put (prefix loc prop value) - "Edit the suffix at LOC in PREFIX, setting PROP to VALUE. -PREFIX is a prefix command, a symbol. -SUFFIX is a suffix command or a group specification (of - the same forms as expected by `transient-define-prefix'). -LOC is a command, a key vector, a key description (a string - as returned by `key-description'), or a coordination list - (whose last element may also be a command or key). -See info node `(transient)Modifying Existing Transients'." - (let ((suf (transient-get-suffix prefix loc))) - (setf (elt suf 2) - (plist-put (elt suf 2) prop value)))) - -(defun transient--layout-member (loc prefix &optional remove) - (let ((val (or (get prefix 'transient--layout) - (error "%s is not a transient command" prefix)))) - (when (listp loc) - (while (integerp (car loc)) - (let* ((children (if (vectorp val) (aref val 3) val)) - (mem (transient--nthcdr (pop loc) children))) - (if (and remove (not loc)) - (let ((rest (delq (car mem) children))) - (if (vectorp val) - (aset val 3 rest) - (put prefix 'transient--layout rest)) - (setq val nil)) - (setq val (if loc (car mem) mem))))) - (setq loc (car loc))) - (if loc - (transient--layout-member-1 (transient--kbd loc) val remove) - val))) - -(defun transient--layout-member-1 (loc layout remove) - (cond ((listp layout) - (seq-some (lambda (elt) (transient--layout-member-1 loc elt remove)) - layout)) - ((vectorp (car (aref layout 3))) - (seq-some (lambda (elt) (transient--layout-member-1 loc elt remove)) - (aref layout 3))) - (remove - (aset layout 3 - (delq (car (transient--group-member loc layout)) - (aref layout 3))) - nil) - ((transient--group-member loc layout)))) - -(defun transient--group-member (loc group) - (cl-member-if (lambda (suffix) - (and (listp suffix) - (let* ((def (nth 2 suffix)) - (cmd (plist-get def :command))) - (if (symbolp loc) - (eq cmd loc) - (equal (transient--kbd - (or (plist-get def :key) - (transient--command-key cmd))) - loc))))) - (aref group 3))) - -(defun transient--kbd (keys) - (when (vectorp keys) - (setq keys (key-description keys))) - (when (stringp keys) - (setq keys (kbd keys))) - keys) - -(defun transient--spec-key (spec) - (let ((plist (nth 2 spec))) - (or (plist-get plist :key) - (transient--command-key - (plist-get plist :command))))) - -(defun transient--command-key (cmd) - (and-let* ((obj (transient--suffix-prototype cmd))) - (cond ((slot-boundp obj 'key) - (oref obj key)) - ((slot-exists-p obj 'shortarg) - (if (slot-boundp obj 'shortarg) - (oref obj shortarg) - (transient--derive-shortarg (oref obj argument))))))) - -(defun transient--nthcdr (n list) - (nthcdr (if (< n 0) (- (length list) (abs n)) n) list)) - -;;; Variables - -(defvar transient-current-prefix nil - "The transient from which this suffix command was invoked. -This is an object representing that transient, use -`transient-current-command' to get the respective command.") - -(defvar transient-current-command nil - "The transient from which this suffix command was invoked. -This is a symbol representing that transient, use -`transient-current-prefix' to get the respective object.") - -(defvar transient-current-suffixes nil - "The suffixes of the transient from which this suffix command was invoked. -This is a list of objects. Usually it is sufficient to instead -use the function `transient-args', which returns a list of -values. In complex cases it might be necessary to use this -variable instead.") - -(defvar transient-exit-hook nil - "Hook run after exiting a transient.") - -(defvar transient-setup-buffer-hook nil - "Hook run when setting up the transient buffer. -That buffer is current and empty when this hook runs.") - -(defvar transient--prefix nil) -(defvar transient--layout nil) -(defvar transient--suffixes nil) - -(defconst transient--stay t "Do not exit the transient.") -(defconst transient--exit nil "Do exit the transient.") - -(defvar transient--exitp nil "Whether to exit the transient.") -(defvar transient--showp nil "Whether to show the transient popup buffer.") -(defvar transient--helpp nil "Whether help-mode is active.") -(defvar transient--editp nil "Whether edit-mode is active.") - -(defvar transient--refreshp nil - "Whether to refresh the transient completely.") - -(defvar transient--all-levels-p nil - "Whether temporary display of suffixes on all levels is active.") - -(defvar transient--timer nil) - -(defvar transient--stack nil) - -(defvar transient--minibuffer-depth 0) - -(defvar transient--buffer-name " *transient*" - "Name of the transient buffer.") - -(defvar transient--buffer nil - "The transient menu buffer.") - -(defvar transient--window nil - "The window used to display the transient popup buffer.") - -(defvar transient--original-window nil - "The window that was selected before the transient was invoked. -Usually it remains selected while the transient is active.") - -(defvar transient--original-buffer nil - "The buffer that was current before the transient was invoked. -Usually it remains current while the transient is active.") - -(defvar transient--restore-winconf nil - "Window configuration to restore after exiting help.") - -(defvar transient--shadowed-buffer nil - "The buffer that is temporarily shadowed by the transient buffer. -This is bound while the suffix predicate is being evaluated and while -drawing in the transient buffer.") - -(defvar transient--pending-suffix nil - "The suffix that is currently being processed. -This is bound while the suffix predicate is being evaluated, -and while functions that return faces are being evaluated.") - -(defvar transient--pending-group nil - "The group that is currently being processed. -This is bound while the suffixes are drawn in the transient buffer.") - -(defvar transient--debug nil - "Whether to put debug information into *Messages*.") - -(defvar transient--history nil) - -(defvar transient--scroll-commands - '(transient-scroll-up - transient-scroll-down - mwheel-scroll - scroll-bar-toolkit-scroll)) - -;;; Identities - -(defun transient-prefix-object () - "Return the current prefix as an object. - -While a transient is being setup or refreshed (which involves -preparing its suffixes) the variable `transient--prefix' can be -used to access the prefix object. Thus this is what has to be -used in suffix methods such as `transient-format-description', -and in object-specific functions that are stored in suffix slots -such as `description'. - -When a suffix command is invoked (i.e., in its `interactive' form -and function body) then the variable `transient-current-prefix' -has to be used instead. - -Two distinct variables are needed, because any prefix may itself -be used as a suffix of another prefix, and such sub-prefixes have -to be able to tell themselves apart from the prefix they were -invoked from. - -Regular suffix commands, which are not prefixes, do not have to -concern themselves with this distinction, so they can use this -function instead. In the context of a plain suffix, it always -returns the value of the appropriate variable." - (or transient--prefix transient-current-prefix)) - -(defun transient-suffix-object (&optional command) - "Return the object associated with the current suffix command. - -Each suffix commands is associated with an object, which holds -additional information about the suffix, such as its value (in -the case of an infix command, which is a kind of suffix command). - -This function is intended to be called by infix commands, which -are usually aliases of `transient--default-infix-command', which -is defined like this: - - (defun transient--default-infix-command () - (interactive) - (let ((obj (transient-suffix-object))) - (transient-infix-set obj (transient-infix-read obj))) - (transient--show)) - -\(User input is read outside of `interactive' to prevent the -command from being added to `command-history'. See #23.) - -Such commands need to be able to access their associated object -to guide how `transient-infix-read' reads the new value and to -store the read value. Other suffix commands (including non-infix -commands) may also need the object to guide their behavior. - -This function attempts to return the object associated with the -current suffix command even if the suffix command was not invoked -from a transient. (For some suffix command that is a valid thing -to do, for others it is not.) In that case nil may be returned, -if the command was not defined using one of the macros intended -to define such commands. - -The optional argument COMMAND is intended for internal use. If -you are contemplating using it in your own code, then you should -probably use this instead: - - (get COMMAND \\='transient--suffix)" - (when command - (cl-check-type command command)) - (cond - (transient--pending-suffix) - ((or transient--prefix - transient-current-prefix) - (let ((suffixes - (cl-remove-if-not - (lambda (obj) - (eq (oref obj command) - (or command - (if (eq this-command 'transient-set-level) - ;; This is how it can look up for which - ;; command it is setting the level. - this-original-command - this-command)))) - (or transient--suffixes - transient-current-suffixes)))) - (or (and (cdr suffixes) - (cl-find-if - (lambda (obj) - (equal (listify-key-sequence (transient--kbd (oref obj key))) - (listify-key-sequence (this-command-keys)))) - suffixes)) - (car suffixes)))) - ((and-let* ((obj (transient--suffix-prototype (or command this-command))) - (obj (clone obj))) - (progn ; work around debbugs#31840 - (transient-init-scope obj) - (transient-init-value obj) - obj))))) - -(defun transient--suffix-prototype (command) - (or (get command 'transient--suffix) - (seq-some (lambda (cmd) (get cmd 'transient--suffix)) - (function-alias-p command)))) - -;;; Keymaps - -(defvar-keymap transient-base-map - :doc "Parent of other keymaps used by Transient. - -This is the parent keymap of all the keymaps that are used in -all transients: `transient-map' (which in turn is the parent -of the transient-specific keymaps), `transient-edit-map' and -`transient-sticky-map'. - -If you change a binding here, then you might also have to edit -`transient-sticky-map' and `transient-common-commands'. While -the latter isn't a proper transient prefix command, it can be -edited using the same functions as used for transients. - -If you add a new command here, then you must also add a binding -to `transient-predicate-map'." - "ESC ESC ESC" #'transient-quit-all - "C-g" #'transient-quit-one - "C-q" #'transient-quit-all - "C-z" #'transient-suspend - "C-v" #'transient-scroll-up - "C-M-v" #'transient-scroll-down - "<next>" #'transient-scroll-up - "<prior>" #'transient-scroll-down) - -(defvar-keymap transient-map - :doc "Top-level keymap used by all transients. - -If you add a new command here, then you must also add a binding -to `transient-predicate-map'. Also see `transient-base-map'." - :parent transient-base-map - "C-u" #'universal-argument - "C--" #'negative-argument - "C-t" #'transient-show - "?" #'transient-help - "C-h" #'transient-help - ;; Also bound to "C-x p" and "C-x n" in transient-common-commands. - "C-M-p" #'transient-history-prev - "C-M-n" #'transient-history-next) - -(defvar-keymap transient-edit-map - :doc "Keymap that is active while a transient in is in \"edit mode\"." - :parent transient-base-map - "?" #'transient-help - "C-h" #'transient-help - "C-x l" #'transient-set-level) - -(defvar-keymap transient-sticky-map - :doc "Keymap that is active while an incomplete key sequence is active." - :parent transient-base-map - "C-g" #'transient-quit-seq) - -(defvar transient--common-command-prefixes '(?\C-x)) - -(put 'transient-common-commands - 'transient--layout - (list - (eval - (car (transient--parse-child - 'transient-common-commands - (vector - :hide - (lambda () - (and (not (memq - (car (bound-and-true-p transient--redisplay-key)) - transient--common-command-prefixes)) - (not transient-show-common-commands))) - (vector - "Value commands" - (list "C-x s " "Set" #'transient-set) - (list "C-x C-s" "Save" #'transient-save) - (list "C-x C-k" "Reset" #'transient-reset) - (list "C-x p " "Previous value" #'transient-history-prev) - (list "C-x n " "Next value" #'transient-history-next)) - (vector - "Sticky commands" - ;; Like `transient-sticky-map' except that - ;; "C-g" has to be bound to a different command. - (list "C-g" "Quit prefix or transient" #'transient-quit-one) - (list "C-q" "Quit transient stack" #'transient-quit-all) - (list "C-z" "Suspend transient stack" #'transient-suspend)) - (vector - "Customize" - (list "C-x t" 'transient-toggle-common :description - (lambda () - (if transient-show-common-commands - "Hide common commands" - "Show common permanently"))) - (list "C-x l" "Show/hide suffixes" #'transient-set-level) - (list "C-x a" #'transient-toggle-level-limit))))) - t))) - -(defvar-keymap transient-popup-navigation-map - :doc "One of the keymaps used when popup navigation is enabled. -See `transient-enable-popup-navigation'." - "<down-mouse-1>" #'transient-noop - "<up>" #'transient-backward-button - "<down>" #'transient-forward-button - "C-r" #'transient-isearch-backward - "C-s" #'transient-isearch-forward - "M-RET" #'transient-push-button) - -(defvar-keymap transient-button-map - :doc "One of the keymaps used when popup navigation is enabled. -See `transient-enable-popup-navigation'." - "<mouse-1>" #'transient-push-button - "<mouse-2>" #'transient-push-button) - -(defvar-keymap transient-resume-mode-map - :doc "Keymap for `transient-resume-mode'. - -This keymap remaps every command that would usually just quit the -documentation buffer to `transient-resume', which additionally -resumes the suspended transient." - "<remap> <Man-quit>" #'transient-resume - "<remap> <Info-exit>" #'transient-resume - "<remap> <quit-window>" #'transient-resume) - -(defvar-keymap transient-predicate-map - :doc "Base keymap used to map common commands to their transient behavior. - -The \"transient behavior\" of a command controls, among other -things, whether invoking the command causes the transient to be -exited or not, and whether infix arguments are exported before -doing so. - -Each \"key\" is a command that is common to all transients and -that is bound in `transient-map', `transient-edit-map', -`transient-sticky-map' and/or `transient-common-command'. - -Each binding is a \"pre-command\", a function that controls the -transient behavior of the respective command. - -For transient commands that are bound in individual transients, -the transient behavior is specified using the `:transient' slot -of the corresponding object." - "<transient-suspend>" #'transient--do-suspend - "<transient-help>" #'transient--do-stay - "<transient-set-level>" #'transient--do-stay - "<transient-history-prev>" #'transient--do-stay - "<transient-history-next>" #'transient--do-stay - "<universal-argument>" #'transient--do-stay - "<universal-argument-more>" #'transient--do-stay - "<negative-argument>" #'transient--do-minus - "<digit-argument>" #'transient--do-stay - "<top-level>" #'transient--do-quit-all - "<transient-quit-all>" #'transient--do-quit-all - "<transient-quit-one>" #'transient--do-quit-one - "<transient-quit-seq>" #'transient--do-stay - "<transient-show>" #'transient--do-stay - "<transient-update>" #'transient--do-stay - "<transient-toggle-common>" #'transient--do-stay - "<transient-set>" #'transient--do-call - "<transient-set-and-exit>" #'transient--do-exit - "<transient-save>" #'transient--do-call - "<transient-save-and-exit>" #'transient--do-exit - "<transient-reset>" #'transient--do-call - "<describe-key-briefly>" #'transient--do-stay - "<describe-key>" #'transient--do-stay - "<transient-scroll-up>" #'transient--do-stay - "<transient-scroll-down>" #'transient--do-stay - "<mwheel-scroll>" #'transient--do-stay - "<scroll-bar-toolkit-scroll>" #'transient--do-stay - "<transient-noop>" #'transient--do-noop - "<transient-mouse-push-button>" #'transient--do-move - "<transient-push-button>" #'transient--do-push-button - "<transient-backward-button>" #'transient--do-move - "<transient-forward-button>" #'transient--do-move - "<transient-isearch-backward>" #'transient--do-move - "<transient-isearch-forward>" #'transient--do-move - ;; If a valid but incomplete prefix sequence is followed by - ;; an unbound key, then Emacs calls the `undefined' command - ;; but does not set `this-command', `this-original-command' - ;; or `real-this-command' accordingly. Instead they are nil. - "<nil>" #'transient--do-warn - ;; Bound to the `mouse-movement' event, this command is similar - ;; to `ignore'. - "<ignore-preserving-kill-region>" #'transient--do-noop) - -(defvar transient--transient-map nil) -(defvar transient--predicate-map nil) -(defvar transient--redisplay-map nil) -(defvar transient--redisplay-key nil) - -(defun transient--push-keymap (var) - (let ((map (symbol-value var))) - (transient--debug " push %s%s" var (if map "" " VOID")) - (when map - (with-demoted-errors "transient--push-keymap: %S" - (internal-push-keymap map 'overriding-terminal-local-map))))) - -(defun transient--pop-keymap (var) - (let ((map (symbol-value var))) - (when map - (transient--debug " pop %s" var) - (with-demoted-errors "transient--pop-keymap: %S" - (internal-pop-keymap map 'overriding-terminal-local-map))))) - -(defun transient--make-transient-map () - (let ((map (make-sparse-keymap))) - (set-keymap-parent map (if transient--editp - transient-edit-map - transient-map)) - (dolist (obj transient--suffixes) - (let ((key (oref obj key))) - (when (vectorp key) - (setq key (key-description key)) - (oset obj key key)) - (when transient-substitute-key-function - (setq key (save-match-data - (funcall transient-substitute-key-function obj))) - (oset obj key key)) - (let* ((kbd (kbd key)) - (cmd (oref obj command)) - (alt (transient--lookup-key map kbd))) - (cond ((not alt) - (define-key map kbd cmd)) - ((eq alt cmd)) - ((transient--inapt-suffix-p obj)) - ((and-let* ((obj (transient-suffix-object alt))) - (transient--inapt-suffix-p obj)) - (define-key map kbd cmd)) - (transient-detect-key-conflicts - (error "Cannot bind %S to %s and also %s" - (string-trim key) cmd alt)) - ((define-key map kbd cmd)))))) - (when-let ((b (keymap-lookup map "-"))) (keymap-set map "<kp-subtract>" b)) - (when-let ((b (keymap-lookup map "="))) (keymap-set map "<kp-equal>" b)) - (when-let ((b (keymap-lookup map "+"))) (keymap-set map "<kp-add>" b)) - (when transient-enable-popup-navigation - ;; `transient--make-redisplay-map' maps only over bindings that are - ;; directly in the base keymap, so that cannot be a composed keymap. - (set-keymap-parent - map (make-composed-keymap - (keymap-parent map) - transient-popup-navigation-map))) - map)) - -(defun transient--make-predicate-map () - (let* ((default (transient--resolve-pre-command - (oref transient--prefix transient-suffix))) - (return (and transient--stack (eq default t))) - (map (make-sparse-keymap))) - (set-keymap-parent map transient-predicate-map) - (when (or (and (slot-boundp transient--prefix 'transient-switch-frame) - (transient--resolve-pre-command - (not (oref transient--prefix transient-switch-frame)))) - (memq (transient--resolve-pre-command - (oref transient--prefix transient-non-suffix)) - '(nil transient--do-warn transient--do-noop))) - (define-key map [handle-switch-frame] #'transient--do-suspend)) - (dolist (obj transient--suffixes) - (let* ((cmd (oref obj command)) - (kind (cond ((get cmd 'transient--prefix) 'prefix) - ((cl-typep obj 'transient-infix) 'infix) - (t 'suffix)))) - (cond - ((oref obj inapt) - (define-key map (vector cmd) #'transient--do-warn-inapt)) - ((slot-boundp obj 'transient) - (define-key map (vector cmd) - (pcase (list kind - (transient--resolve-pre-command (oref obj transient)) - return) - (`(prefix t ,_) #'transient--do-recurse) - (`(prefix nil ,_) #'transient--do-stack) - (`(infix t ,_) #'transient--do-stay) - (`(suffix t ,_) #'transient--do-call) - ('(suffix nil t) #'transient--do-return) - (`(,_ nil ,_) #'transient--do-exit) - (`(,_ ,do ,_) do)))) - ((not (lookup-key transient-predicate-map (vector cmd))) - (define-key map (vector cmd) - (pcase (list kind default return) - (`(prefix ,(or 'transient--do-stay 'transient--do-call) ,_) - #'transient--do-recurse) - (`(prefix t ,_) #'transient--do-recurse) - (`(prefix ,_ ,_) #'transient--do-stack) - (`(infix ,_ ,_) #'transient--do-stay) - (`(suffix t ,_) #'transient--do-call) - ('(suffix nil t) #'transient--do-return) - (`(suffix nil ,_) #'transient--do-exit) - (`(suffix ,do ,_) do))))))) - map)) - -(defun transient--make-redisplay-map () - (setq transient--redisplay-key - (pcase this-command - ('transient-update - (setq transient--showp t) - (setq unread-command-events - (listify-key-sequence (this-single-command-raw-keys)))) - ('transient-quit-seq - (setq unread-command-events - (butlast (listify-key-sequence - (this-single-command-raw-keys)) - 2)) - (butlast transient--redisplay-key)) - (_ nil))) - (let ((topmap (make-sparse-keymap)) - (submap (make-sparse-keymap))) - (when transient--redisplay-key - (define-key topmap (vconcat transient--redisplay-key) submap) - (set-keymap-parent submap transient-sticky-map)) - (map-keymap-internal - (lambda (key def) - (when (and (not (eq key ?\e)) - (listp def) - (keymapp def)) - (define-key topmap (vconcat transient--redisplay-key (list key)) - #'transient-update))) - (if transient--redisplay-key - (let ((key (vconcat transient--redisplay-key))) - (or (lookup-key transient--transient-map key) - (and-let* ((regular (lookup-key local-function-key-map key))) - (lookup-key transient--transient-map (vconcat regular))))) - transient--transient-map)) - topmap)) - -;;; Setup - -(defun transient-setup (&optional name layout edit &rest params) - "Setup the transient specified by NAME. - -This function is called by transient prefix commands to setup the -transient. In that case NAME is mandatory, LAYOUT and EDIT must -be nil and PARAMS may be (but usually is not) used to set, e.g., -the \"scope\" of the transient (see `transient-define-prefix'). - -This function is also called internally in which case LAYOUT and -EDIT may be non-nil." - (transient--debug 'setup) - (transient--with-emergency-exit :setup - (cond - ((not name) - ;; Switching between regular and edit mode. - (transient--pop-keymap 'transient--transient-map) - (transient--pop-keymap 'transient--redisplay-map) - (setq name (oref transient--prefix command)) - (setq params (list :scope (oref transient--prefix scope)))) - (transient--prefix - ;; Invoked as a ":transient-non-suffix 'transient--do-{stay,call}" - ;; of an outer prefix. Unlike the usual `transient--do-stack', - ;; these predicates fail to clean up after the outer prefix. - (transient--pop-keymap 'transient--transient-map) - (transient--pop-keymap 'transient--redisplay-map)) - ((not (or layout ; resuming parent/suspended prefix - transient-current-command)) ; entering child prefix - (transient--stack-zap)) ; replace suspended prefix, if any - (edit - ;; Returning from help to edit. - (setq transient--editp t))) - (transient--init-objects name layout params) - (transient--init-keymaps) - (transient--history-init transient--prefix) - (setq transient--original-window (selected-window)) - (setq transient--original-buffer (current-buffer)) - (setq transient--minibuffer-depth (minibuffer-depth)) - (transient--redisplay) - (transient--init-transient) - (transient--suspend-which-key-mode))) - -(cl-defgeneric transient-setup-children (group children) - "Setup the CHILDREN of GROUP. -If the value of the `setup-children' slot is non-nil, then call -that function with CHILDREN as the only argument and return the -value. Otherwise return CHILDREN as is." - (if (slot-boundp group 'setup-children) - (funcall (oref group setup-children) children) - children)) - -(defun transient--init-keymaps () - (setq transient--predicate-map (transient--make-predicate-map)) - (setq transient--transient-map (transient--make-transient-map)) - (setq transient--redisplay-map (transient--make-redisplay-map))) - -(defun transient--init-objects (&optional name layout params) - (if name - (setq transient--prefix (transient--init-prefix name params)) - (setq name (oref transient--prefix command))) - (setq transient--refreshp (oref transient--prefix refresh-suffixes)) - (setq transient--layout (or layout (transient--init-suffixes name))) - (setq transient--suffixes (transient--flatten-suffixes transient--layout))) - -(defun transient--init-prefix (name &optional params) - (let ((obj (let ((proto (get name 'transient--prefix))) - (apply #'clone proto - :prototype proto - :level (or (alist-get t (alist-get name transient-levels)) - transient-default-level) - params)))) - (transient--setup-recursion obj) - (transient-init-value obj) - obj)) - -(defun transient--init-suffixes (name) - (let ((levels (alist-get name transient-levels))) - (cl-mapcan (lambda (c) (transient--init-child levels c nil)) - (append (get name 'transient--layout) - (and (not transient--editp) - (get 'transient-common-commands - 'transient--layout)))))) - -(defun transient--flatten-suffixes (layout) - (cl-labels ((s (def) - (cond - ((stringp def) nil) - ((cl-typep def 'transient-information) nil) - ((listp def) (cl-mapcan #'s def)) - ((cl-typep def 'transient-group) - (cl-mapcan #'s (oref def suffixes))) - ((cl-typep def 'transient-suffix) - (list def))))) - (cl-mapcan #'s layout))) - -(defun transient--init-child (levels spec parent) - (cl-etypecase spec - (vector (transient--init-group levels spec parent)) - (list (transient--init-suffix levels spec parent)) - (string (list spec)))) - -(defun transient--init-group (levels spec parent) - (pcase-let ((`(,level ,class ,args ,children) (append spec nil))) - (and-let* (((transient--use-level-p level)) - (obj (apply class :level level args)) - ((transient--use-suffix-p obj)) - ((prog1 t - (when (or (and parent (oref parent inapt)) - (transient--inapt-suffix-p obj)) - (oset obj inapt t)))) - (suffixes (cl-mapcan - (lambda (c) (transient--init-child levels c obj)) - (transient-setup-children obj children)))) - (progn ; work around debbugs#31840 - (oset obj suffixes suffixes) - (list obj))))) - -(defun transient--init-suffix (levels spec parent) - (pcase-let* ((`(,level ,class ,args) spec) - (cmd (plist-get args :command)) - (key (transient--kbd (plist-get args :key))) - (level (or (alist-get (cons cmd key) levels nil nil #'equal) - (alist-get cmd levels) - level))) - (let ((fn (and (symbolp cmd) - (symbol-function cmd)))) - (when (autoloadp fn) - (transient--debug " autoload %s" cmd) - (autoload-do-load fn))) - (when (transient--use-level-p level) - (let ((obj (if (child-of-class-p class 'transient-information) - (apply class :level level args) - (unless (and cmd (symbolp cmd)) - (error "BUG: Non-symbolic suffix command: %s" cmd)) - (if-let ((proto (and cmd (transient--suffix-prototype cmd)))) - (apply #'clone proto :level level args) - (apply class :command cmd :level level args))))) - (cond ((not cmd)) - ((commandp cmd)) - ((or (cl-typep obj 'transient-switch) - (cl-typep obj 'transient-option)) - ;; As a temporary special case, if the package was compiled - ;; with an older version of Transient, then we must define - ;; "anonymous" switch and option commands here. - (defalias cmd #'transient--default-infix-command)) - ((transient--use-suffix-p obj) - (error "Suffix command %s is not defined or autoloaded" cmd))) - (unless (cl-typep obj 'transient-information) - (transient--init-suffix-key obj)) - (when (transient--use-suffix-p obj) - (if (or (and parent (oref parent inapt)) - (transient--inapt-suffix-p obj)) - (oset obj inapt t) - (transient-init-scope obj) - (transient-init-value obj)) - (list obj)))))) - -(cl-defmethod transient--init-suffix-key ((obj transient-suffix)) - (unless (slot-boundp obj 'key) - (error "No key for %s" (oref obj command)))) - -(cl-defmethod transient--init-suffix-key ((obj transient-argument)) - (if (transient-switches--eieio-childp obj) - (cl-call-next-method obj) - (unless (slot-boundp obj 'shortarg) - (when-let ((shortarg (transient--derive-shortarg (oref obj argument)))) - (oset obj shortarg shortarg))) - (unless (slot-boundp obj 'key) - (if (slot-boundp obj 'shortarg) - (oset obj key (oref obj shortarg)) - (error "No key for %s" (oref obj command)))))) - -(defun transient--use-level-p (level &optional edit) - (or transient--all-levels-p - (and transient--editp (not edit)) - (and (>= level 1) - (<= level (oref transient--prefix level))))) - -(defun transient--use-suffix-p (obj) - (let ((transient--shadowed-buffer (current-buffer)) - (transient--pending-suffix obj)) - (transient--do-suffix-p - (oref obj if) - (oref obj if-not) - (oref obj if-nil) - (oref obj if-non-nil) - (oref obj if-mode) - (oref obj if-not-mode) - (oref obj if-derived) - (oref obj if-not-derived) - t))) - -(defun transient--inapt-suffix-p (obj) - (let ((transient--shadowed-buffer (current-buffer)) - (transient--pending-suffix obj)) - (transient--do-suffix-p - (oref obj inapt-if) - (oref obj inapt-if-not) - (oref obj inapt-if-nil) - (oref obj inapt-if-non-nil) - (oref obj inapt-if-mode) - (oref obj inapt-if-not-mode) - (oref obj inapt-if-derived) - (oref obj inapt-if-not-derived) - nil))) - -(defun transient--do-suffix-p - (if if-not if-nil if-non-nil if-mode if-not-mode if-derived if-not-derived - default) - (cond - (if (funcall if)) - (if-not (not (funcall if-not))) - (if-non-nil (symbol-value if-non-nil)) - (if-nil (not (symbol-value if-nil))) - (if-mode (if (atom if-mode) - (eq major-mode if-mode) - (memq major-mode if-mode))) - (if-not-mode (not (if (atom if-not-mode) - (eq major-mode if-not-mode) - (memq major-mode if-not-mode)))) - (if-derived (if (or (atom if-derived) - (>= emacs-major-version 30)) - (derived-mode-p if-derived) - (apply #'derived-mode-p if-derived))) - (if-not-derived (not (if (or (atom if-not-derived) - (>= emacs-major-version 30)) - (derived-mode-p if-not-derived) - (apply #'derived-mode-p if-not-derived)))) - (default))) - -(defun transient--suffix-predicate (spec) - (let ((plist (nth 2 spec))) - (seq-some (lambda (prop) - (and-let* ((pred (plist-get plist prop))) - (list prop pred))) - '( :if :if-not - :if-nil :if-non-nil - :if-mode :if-not-mode - :if-derived :if-not-derived - :inapt-if :inapt-if-not - :inapt-if-nil :inapt-if-non-nil - :inapt-if-mode :inapt-if-not-mode - :inapt-if-derived :inapt-if-not-derived)))) - -;;; Flow-Control - -(defun transient--init-transient () - (transient--debug 'init-transient) - (transient--push-keymap 'transient--transient-map) - (transient--push-keymap 'transient--redisplay-map) - (add-hook 'pre-command-hook #'transient--pre-command) - (add-hook 'post-command-hook #'transient--post-command) - (advice-add 'recursive-edit :around #'transient--recursive-edit) - (when transient--exitp - ;; This prefix command was invoked as the suffix of another. - ;; Prevent `transient--post-command' from removing the hooks - ;; that we just added. - (setq transient--exitp 'replace))) - -(defun transient--refresh-transient () - (transient--debug 'refresh-transient) - (transient--pop-keymap 'transient--predicate-map) - (transient--pop-keymap 'transient--transient-map) - (transient--pop-keymap 'transient--redisplay-map) - (transient--init-objects) - (transient--init-keymaps) - (transient--push-keymap 'transient--transient-map) - (transient--push-keymap 'transient--redisplay-map) - (transient--redisplay)) - -(defun transient--pre-command () - (transient--debug 'pre-command) - (transient--with-emergency-exit :pre-command - ;; The use of `overriding-terminal-local-map' does not prevent the - ;; lookup of command remappings in the overridden maps, which can - ;; lead to a suffix being remapped to a non-suffix. We have to undo - ;; the remapping in that case. However, remapping a non-suffix to - ;; another should remain possible. - (when (and (transient--get-pre-command this-original-command 'suffix) - (not (transient--get-pre-command this-command 'suffix))) - (setq this-command this-original-command)) - (cond - ((memq this-command '(transient-update transient-quit-seq)) - (transient--pop-keymap 'transient--redisplay-map)) - ((and transient--helpp - (not (memq this-command '(transient-quit-one - transient-quit-all)))) - (cond - ((transient-help) - (transient--do-suspend) - (setq this-command 'transient-suspend) - (transient--pre-exit)) - ((not (transient--edebug-command-p)) - (setq this-command 'transient-undefined)))) - ((and transient--editp - (transient-suffix-object) - (not (memq this-command '(transient-quit-one - transient-quit-all - transient-help)))) - (setq this-command 'transient-set-level) - (transient--wrap-command)) - (t - (setq transient--exitp nil) - (let ((exitp (eq (transient--call-pre-command) transient--exit))) - (transient--wrap-command) - (when exitp - (transient--pre-exit))))))) - -(defun transient--pre-exit () - (transient--debug 'pre-exit) - (transient--delete-window) - (transient--timer-cancel) - (transient--pop-keymap 'transient--transient-map) - (transient--pop-keymap 'transient--redisplay-map) - (unless transient--showp - (let ((message-log-max nil)) - (message ""))) - (setq transient--transient-map nil) - (setq transient--predicate-map nil) - (setq transient--redisplay-map nil) - (setq transient--redisplay-key nil) - (setq transient--helpp nil) - (setq transient--editp nil) - (setq transient--prefix nil) - (setq transient--layout nil) - (setq transient--suffixes nil) - (setq transient--original-window nil) - (setq transient--original-buffer nil) - (setq transient--window nil)) - -(defun transient--delete-window () - (when (window-live-p transient--window) - (let ((remain-in-minibuffer-window - (and (minibuffer-selected-window) - (selected-window)))) - ;; Only delete the window if it has never shown another buffer. - (unless (eq (car (window-parameter transient--window 'quit-restore)) - 'other) - (with-demoted-errors "Error while exiting transient: %S" - (delete-window transient--window))) - (when (buffer-live-p transient--buffer) - (kill-buffer transient--buffer)) - (setq transient--buffer nil) - (when remain-in-minibuffer-window - (select-window remain-in-minibuffer-window))))) - -(defun transient--export () - (setq transient-current-prefix transient--prefix) - (setq transient-current-command (oref transient--prefix command)) - (setq transient-current-suffixes transient--suffixes) - (transient--history-push transient--prefix)) - -(defun transient--suspend-override (&optional nohide) - (transient--debug 'suspend-override) - (transient--timer-cancel) - (cond ((and (not nohide) transient-hide-during-minibuffer-read) - (transient--delete-window)) - ((and transient--prefix transient--redisplay-key) - (setq transient--redisplay-key nil) - (when transient--showp - (if-let ((win (minibuffer-selected-window))) - (with-selected-window win - (transient--show)) - (transient--show))))) - (transient--pop-keymap 'transient--transient-map) - (transient--pop-keymap 'transient--redisplay-map) - (remove-hook 'pre-command-hook #'transient--pre-command) - (remove-hook 'post-command-hook #'transient--post-command)) - -(defun transient--resume-override (&optional _ignore) - (transient--debug 'resume-override) - (when (and transient--showp transient-hide-during-minibuffer-read) - (transient--show)) - (transient--push-keymap 'transient--transient-map) - (transient--push-keymap 'transient--redisplay-map) - (add-hook 'pre-command-hook #'transient--pre-command) - (add-hook 'post-command-hook #'transient--post-command)) - -(defun transient--recursive-edit (fn) - (transient--debug 'recursive-edit) - (if (not transient--prefix) - (funcall fn) - (transient--suspend-override (bound-and-true-p edebug-active)) - (funcall fn) ; Already unwind protected. - (cond ((memq this-command '(top-level abort-recursive-edit)) - (setq transient--exitp t) - (transient--post-exit) - (transient--delete-window)) - (transient--prefix - (transient--resume-override))))) - -(defmacro transient--with-suspended-override (&rest body) - (let ((depth (make-symbol "depth")) - (setup (make-symbol "setup")) - (exit (make-symbol "exit"))) - `(if (and transient--transient-map - (memq transient--transient-map - overriding-terminal-local-map)) - (let ((,depth (1+ (minibuffer-depth))) ,setup ,exit) - (setq ,setup - (lambda () "@transient--with-suspended-override" - (transient--debug 'minibuffer-setup) - (remove-hook 'minibuffer-setup-hook ,setup) - (transient--suspend-override))) - (setq ,exit - (lambda () "@transient--with-suspended-override" - (transient--debug 'minibuffer-exit) - (when (= (minibuffer-depth) ,depth) - (transient--resume-override)))) - (unwind-protect - (progn - (add-hook 'minibuffer-setup-hook ,setup) - (add-hook 'minibuffer-exit-hook ,exit) - ,@body) - (remove-hook 'minibuffer-setup-hook ,setup) - (remove-hook 'minibuffer-exit-hook ,exit))) - ,@body))) - -(static-if (>= emacs-major-version 30) ;transient--wrap-command - (defun transient--wrap-command () - (cl-assert - (>= emacs-major-version 30) nil - "Emacs was downgraded, making it necessary to recompile Transient") - (letrec - ((prefix transient--prefix) - (suffix this-command) - (advice - (lambda (fn &rest args) - (interactive - (lambda (spec) - (let ((abort t)) - (unwind-protect - (prog1 (let ((debugger #'transient--exit-and-debug)) - (advice-eval-interactive-spec spec)) - (setq abort nil)) - (when abort - (when-let ((unwind (oref prefix unwind-suffix))) - (transient--debug 'unwind-interactive) - (funcall unwind suffix)) - (advice-remove suffix advice) - (oset prefix unwind-suffix nil)))))) - (unwind-protect - (let ((debugger #'transient--exit-and-debug)) - (apply fn args)) - (when-let ((unwind (oref prefix unwind-suffix))) - (transient--debug 'unwind-command) - (funcall unwind suffix)) - (advice-remove suffix advice) - (oset prefix unwind-suffix nil))))) - (when (symbolp this-command) - (advice-add suffix :around advice '((depth . -99)))))) - - (defun transient--wrap-command () - (let* ((prefix transient--prefix) - (suffix this-command) - (advice nil) - (advice-interactive - (lambda (spec) - (let ((abort t)) - (unwind-protect - (prog1 (let ((debugger #'transient--exit-and-debug)) - (advice-eval-interactive-spec spec)) - (setq abort nil)) - (when abort - (when-let ((unwind (oref prefix unwind-suffix))) - (transient--debug 'unwind-interactive) - (funcall unwind suffix)) - (advice-remove suffix advice) - (oset prefix unwind-suffix nil)))))) - (advice-body - (lambda (fn &rest args) - (unwind-protect - (let ((debugger #'transient--exit-and-debug)) - (apply fn args)) - (when-let ((unwind (oref prefix unwind-suffix))) - (transient--debug 'unwind-command) - (funcall unwind suffix)) - (advice-remove suffix advice) - (oset prefix unwind-suffix nil))))) - (setq advice `(lambda (fn &rest args) - (interactive ,advice-interactive) - (apply ',advice-body fn args))) - (when (symbolp this-command) - (advice-add suffix :around advice '((depth . -99))))))) - -(defun transient--premature-post-command () - (and (equal (this-command-keys-vector) []) - (= (minibuffer-depth) - (1+ transient--minibuffer-depth)) - (progn - (transient--debug 'premature-post-command) - (transient--suspend-override) - (oset (or transient--prefix transient-current-prefix) - unwind-suffix - (if transient--exitp - #'transient--post-exit - #'transient--resume-override)) - t))) - -(defun transient--post-command () - (unless (transient--premature-post-command) - (transient--debug 'post-command) - (transient--with-emergency-exit :post-command - (cond (transient--exitp (transient--post-exit)) - ;; If `this-command' is the current transient prefix, then we - ;; have already taken care of updating the transient buffer... - ((and (eq this-command (oref transient--prefix command)) - ;; ... but if `prefix-arg' is non-nil, then the values - ;; of `this-command' and `real-this-command' are untrue - ;; because `prefix-command-preserve-state' changes them. - ;; We cannot use `current-prefix-arg' because it is set - ;; too late (in `command-execute'), and if it were set - ;; earlier, then we likely still would not be able to - ;; rely on it and `prefix-command-preserve-state-hook' - ;; would have to be used to record that a universal - ;; argument is in effect. - (not prefix-arg))) - (transient--refreshp - (transient--refresh-transient)) - ((let ((old transient--redisplay-map) - (new (transient--make-redisplay-map))) - (unless (equal old new) - (transient--pop-keymap 'transient--redisplay-map) - (setq transient--redisplay-map new) - (transient--push-keymap 'transient--redisplay-map)) - (transient--redisplay))))))) - -(defun transient--post-exit (&optional command) - (transient--debug 'post-exit) - (unless (and (eq transient--exitp 'replace) - (or transient--prefix - ;; The current command could act as a prefix, - ;; but decided not to call `transient-setup', - ;; or it is prevented from doing so because it - ;; uses the minibuffer and the user aborted - ;; that. - (prog1 nil - (if (let ((obj (transient-suffix-object command))) - (and (slot-boundp obj 'transient) - (oref obj transient))) - ;; This sub-prefix is a transient suffix; - ;; go back to outer prefix, by calling - ;; `transient--stack-pop' further down. - (setq transient--exitp nil) - (transient--stack-zap))))) - (remove-hook 'pre-command-hook #'transient--pre-command) - (remove-hook 'post-command-hook #'transient--post-command) - (advice-remove 'recursive-edit #'transient--recursive-edit)) - (setq transient-current-prefix nil) - (setq transient-current-command nil) - (setq transient-current-suffixes nil) - (let ((resume (and transient--stack - (not (memq transient--exitp '(replace suspend)))))) - (unless (or resume (eq transient--exitp 'replace)) - (setq transient--showp nil)) - (setq transient--exitp nil) - (setq transient--helpp nil) - (setq transient--editp nil) - (setq transient--all-levels-p nil) - (setq transient--minibuffer-depth 0) - (run-hooks 'transient-exit-hook) - (when resume - (transient--stack-pop)))) - -(defun transient--stack-push () - (transient--debug 'stack-push) - (push (list (oref transient--prefix command) - transient--layout - transient--editp - :transient-suffix (oref transient--prefix transient-suffix) - :scope (oref transient--prefix scope)) - transient--stack)) - -(defun transient--stack-pop () - (transient--debug 'stack-pop) - (and transient--stack - (prog1 t (apply #'transient-setup (pop transient--stack))))) - -(defun transient--stack-zap () - (transient--debug 'stack-zap) - (setq transient--stack nil)) - -(defun transient--redisplay () - (if (or (eq transient-show-popup t) - transient--showp) - (unless - (or (memq this-command transient--scroll-commands) - (and (or (memq this-command '(mouse-drag-region - mouse-set-region)) - (equal (key-description (this-command-keys-vector)) - "<mouse-movement>")) - (and (eq (current-buffer) transient--buffer)))) - (transient--show)) - (when (and (numberp transient-show-popup) - (not (zerop transient-show-popup)) - (not transient--timer)) - (transient--timer-start)) - (transient--show-brief))) - -(defun transient--timer-start () - (setq transient--timer - (run-at-time (abs transient-show-popup) nil - (lambda () - (transient--timer-cancel) - (transient--show) - (let ((message-log-max nil)) - (message "")))))) - -(defun transient--timer-cancel () - (when transient--timer - (cancel-timer transient--timer) - (setq transient--timer nil))) - -(defun transient--debug (arg &rest args) - (when transient--debug - (let ((inhibit-message (not (eq transient--debug 'message)))) - (if (symbolp arg) - (message "-- %-22s (cmd: %s, event: %S, exit: %s%s)" - arg - (cond ((and (symbolp this-command) this-command)) - ((fboundp 'help-fns-function-name) - (help-fns-function-name this-command)) - ((byte-code-function-p this-command) - "#[...]") - (this-command)) - (key-description (this-command-keys-vector)) - transient--exitp - (cond ((keywordp (car args)) - (format ", from: %s" - (substring (symbol-name (car args)) 1))) - ((stringp (car args)) - (concat ", " (apply #'format args))) - ((functionp (car args)) - (concat ", " (apply (car args) (cdr args)))) - (""))) - (apply #'message arg args))))) - -(defun transient--emergency-exit (&optional id) - "Exit the current transient command after an error occurred. -When no transient is active (i.e., when `transient--prefix' is -nil) then do nothing. Optional ID is a keyword identifying the -exit." - (transient--debug 'emergency-exit id) - (when transient--prefix - (setq transient--stack nil) - (setq transient--exitp t) - (transient--pre-exit) - (transient--post-exit))) - -;;; Pre-Commands - -(defun transient--call-pre-command () - (if-let ((fn (transient--get-pre-command this-command))) - (let ((action (funcall fn))) - (when (eq action transient--exit) - (setq transient--exitp (or transient--exitp t))) - action) - (if (let ((keys (this-command-keys-vector))) - (eq (aref keys (1- (length keys))) ?\C-g)) - (setq this-command 'transient-noop) - (unless (transient--edebug-command-p) - (setq this-command 'transient-undefined))) - transient--stay)) - -(defun transient--get-pre-command (&optional cmd enforce-type) - (or (and (not (eq enforce-type 'non-suffix)) - (symbolp cmd) - (lookup-key transient--predicate-map (vector cmd))) - (and (not (eq enforce-type 'suffix)) - (transient--resolve-pre-command - (oref transient--prefix transient-non-suffix) - t)))) - -(defun transient--resolve-pre-command (pre &optional resolve-boolean) - (cond ((booleanp pre) - (if resolve-boolean - (if pre #'transient--do-stay #'transient--do-warn) - pre)) - ((string-match-p "--do-" (symbol-name pre)) pre) - ((let ((sym (intern (format "transient--do-%s" pre)))) - (if (functionp sym) sym pre))))) - -(defun transient--do-stay () - "Call the command without exporting variables and stay transient." - transient--stay) - -(defun transient--do-noop () - "Call `transient-noop' and stay transient." - (setq this-command 'transient-noop) - transient--stay) - -(defun transient--do-warn () - "Call `transient-undefined' and stay transient." - (setq this-command 'transient-undefined) - transient--stay) - -(defun transient--do-warn-inapt () - "Call `transient-inapt' and stay transient." - (setq this-command 'transient-inapt) - transient--stay) - -(defun transient--do-call () - "Call the command after exporting variables and stay transient." - (transient--export) - transient--stay) - -(defun transient--do-return () - "Call the command after exporting variables and return to parent prefix. -If there is no parent prefix, then behave like `transient--do-exit'." - (if (not transient--stack) - (transient--do-exit) - (transient--export) - transient--exit)) - -(defun transient--do-exit () - "Call the command after exporting variables and exit the transient." - (transient--export) - (transient--stack-zap) - transient--exit) - -(defun transient--do-leave () - "Call the command without exporting variables and exit the transient." - (transient--stack-zap) - transient--exit) - -(defun transient--do-push-button () - "Call the command represented by the activated button. -Use that command's pre-command to determine transient behavior." - (if (and (mouse-event-p last-command-event) - (not (eq (posn-window (event-start last-command-event)) - transient--window))) - transient--stay - (setq this-command - (with-selected-window transient--window - (get-text-property (if (mouse-event-p last-command-event) - (posn-point (event-start last-command-event)) - (point)) - 'command))) - (transient--call-pre-command))) - -(defun transient--do-recurse () - "Call the transient prefix command, preparing for return to active transient. -If there is no parent prefix, then just call the command." - (transient--do-stack)) - -(defun transient--setup-recursion (prefix-obj) - (when transient--stack - (let ((command (oref prefix-obj command))) - (when-let ((suffix-obj (transient-suffix-object command))) - (when (memq (if (slot-boundp suffix-obj 'transient) - (oref suffix-obj transient) - (oref transient-current-prefix transient-suffix)) - (list t #'transient--do-recurse)) - (oset prefix-obj transient-suffix t)))))) - -(defun transient--do-stack () - "Call the transient prefix command, stacking the active transient. -Push the active transient to the transient stack." - (transient--export) - (transient--stack-push) - (setq transient--exitp 'replace) - transient--exit) - -(defun transient--do-replace () - "Call the transient prefix command, replacing the active transient. -Do not push the active transient to the transient stack." - (transient--export) - (setq transient--exitp 'replace) - transient--exit) - -(defun transient--do-suspend () - "Suspend the active transient, saving the transient stack." - (transient--stack-push) - (setq transient--exitp 'suspend) - transient--exit) - -(defun transient--do-quit-one () - "If active, quit help or edit mode, else exit the active transient." - (cond (transient--helpp - (setq transient--helpp nil) - transient--stay) - (transient--editp - (setq transient--editp nil) - (transient-setup) - transient--stay) - (prefix-arg - transient--stay) - (transient--exit))) - -(defun transient--do-quit-all () - "Exit all transients without saving the transient stack." - (transient--stack-zap) - transient--exit) - -(defun transient--do-move () - "Call the command if `transient-enable-popup-navigation' is non-nil. -In that case behave like `transient--do-stay', otherwise similar -to `transient--do-warn'." - (unless transient-enable-popup-navigation - (setq this-command 'transient-inhibit-move)) - transient--stay) - -(defun transient--do-minus () - "Call `negative-argument' or pivot to `transient-update'. -If `negative-argument' is invoked using \"-\" then preserve the -prefix argument and pivot to `transient-update'." - (when (equal (this-command-keys) "-") - (setq this-command 'transient-update)) - transient--stay) - -(put 'transient--do-stay 'transient-face 'transient-key-stay) -(put 'transient--do-noop 'transient-face 'transient-key-noop) -(put 'transient--do-warn 'transient-face 'transient-key-noop) -(put 'transient--do-warn-inapt 'transient-face 'transient-key-noop) -(put 'transient--do-call 'transient-face 'transient-key-stay) -(put 'transient--do-return 'transient-face 'transient-key-return) -(put 'transient--do-exit 'transient-face 'transient-key-exit) -(put 'transient--do-leave 'transient-face 'transient-key-exit) - -(put 'transient--do-recurse 'transient-face 'transient-key-stay) -(put 'transient--do-stack 'transient-face 'transient-key-stay) -(put 'transient--do-replace 'transient-face 'transient-key-exit) -(put 'transient--do-suspend 'transient-face 'transient-key-exit) - -(put 'transient--do-quit-one 'transient-face 'transient-key-return) -(put 'transient--do-quit-all 'transient-face 'transient-key-exit) -(put 'transient--do-move 'transient-face 'transient-key-stay) -(put 'transient--do-minus 'transient-face 'transient-key-stay) - -;;; Commands -;;;; Noop - -(defun transient-noop () - "Do nothing at all." - (interactive)) - -(defun transient-undefined () - "Warn the user that the pressed key is not bound to any suffix." - (interactive) - (transient--invalid "Unbound suffix")) - -(defun transient-inapt () - "Warn the user that the invoked command is inapt." - (interactive) - (transient--invalid "Inapt command")) - -(defun transient--invalid (msg) - (ding) - (message "%s: `%s' (Use `%s' to abort, `%s' for help)%s" - msg - (propertize (key-description (this-single-command-keys)) - 'face 'font-lock-warning-face) - (propertize "C-g" 'face 'transient-key) - (propertize "?" 'face 'transient-key) - ;; `this-command' is `transient-undefined' or `transient-inapt'. - ;; Show the command (`this-original-command') the user actually - ;; tried to invoke. - (if-let ((cmd (or (ignore-errors (symbol-name this-original-command)) - (ignore-errors (symbol-name this-command))))) - (format " [%s]" (propertize cmd 'face 'font-lock-warning-face)) - "")) - (unless (and transient--transient-map - (memq transient--transient-map overriding-terminal-local-map)) - (let ((transient--prefix (or transient--prefix 'sic))) - (transient--emergency-exit)) - (view-lossage) - (other-window 1) - (display-warning 'transient "Inconsistent transient state detected. -This should never happen. -Please open an issue and post the shown command log." :error))) - -(defun transient-inhibit-move () - "Warn the user that popup navigation is disabled." - (interactive) - (message "To enable use of `%s', please customize `%s'" - this-original-command - 'transient-enable-popup-navigation)) - -;;;; Core - -(defun transient-quit-all () - "Exit all transients without saving the transient stack." - (interactive)) - -(defun transient-quit-one () - "Exit the current transients, returning to outer transient, if any." - (interactive)) - -(defun transient-quit-seq () - "Abort the current incomplete key sequence." - (interactive)) - -(defun transient-update () - "Redraw the transient's state in the popup buffer." - (interactive) - (setq prefix-arg current-prefix-arg)) - -(defun transient-show () - "Show the transient's state in the popup buffer." - (interactive) - (setq transient--showp t)) - -(defun transient-push-button () - "Invoke the suffix command represented by this button." - (interactive)) - -;;;; Suspend - -(defun transient-suspend () - "Suspend the current transient. -It can later be resumed using `transient-resume', while no other -transient is active." - (interactive)) - -(define-minor-mode transient-resume-mode - "Auxiliary minor-mode used to resume a transient after viewing help.") - -(defun transient-resume () - "Resume a previously suspended stack of transients." - (interactive) - (cond (transient--stack - (let ((winconf transient--restore-winconf)) - (kill-local-variable 'transient--restore-winconf) - (when transient-resume-mode - (transient-resume-mode -1) - (quit-window)) - (when winconf - (set-window-configuration winconf))) - (transient--stack-pop)) - (transient-resume-mode - (kill-local-variable 'transient--restore-winconf) - (transient-resume-mode -1) - (quit-window)) - (t - (message "No suspended transient command")))) - -;;;; Help - -(defun transient-help (&optional interactive) - "Show help for the active transient or one of its suffixes.\n\n(fn)" - (interactive (list t)) - (if interactive - (setq transient--helpp t) - (with-demoted-errors "transient-help: %S" - (when (lookup-key transient--transient-map - (this-single-command-raw-keys)) - (setq transient--helpp nil) - (let ((winconf (current-window-configuration))) - (transient-show-help - (if (eq this-original-command 'transient-help) - transient--prefix - (or (transient-suffix-object) - this-original-command))) - (setq-local transient--restore-winconf winconf)) - (fit-window-to-buffer nil (frame-height) (window-height)) - (transient-resume-mode) - (message (substitute-command-keys - "Type \\`q' to resume transient command.")) - t)))) - -;;;; Level - -(defun transient-set-level (&optional command level) - "Set the level of the transient or one of its suffix commands." - (interactive - (let ((command this-original-command) - (prefix (oref transient--prefix command))) - (and (or (not (eq command 'transient-set-level)) - (and transient--editp - (setq command prefix))) - (list command - (let ((keys (this-single-command-raw-keys))) - (and (lookup-key transient--transient-map keys) - (progn - (transient--show) - (string-to-number - (transient--read-number-N - (format "Set level for `%s': " command) - nil nil (not (eq command prefix))))))))))) - (cond - ((not command) - (setq transient--editp t) - (transient-setup)) - (level - (let* ((prefix (oref transient--prefix command)) - (alist (alist-get prefix transient-levels)) - (akey command)) - (cond ((eq command prefix) - (oset transient--prefix level level) - (setq akey t)) - (t - (oset (transient-suffix-object command) level level) - (when (cdr (cl-remove-if-not (lambda (obj) - (eq (oref obj command) command)) - transient--suffixes)) - (setq akey (cons command (this-command-keys)))))) - (setf (alist-get akey alist) level) - (setf (alist-get prefix transient-levels) alist)) - (transient-save-levels) - (transient--show)) - (t - (transient-undefined)))) - -(transient-define-suffix transient-toggle-level-limit () - "Toggle whether to temporarily displayed suffixes on all levels." - :description - (lambda () - (cond - ((= transient-default-level transient--max-level) - "Always displaying all levels") - (transient--all-levels-p - (format "Hide suffix %s" - (propertize - (format "levels > %s" (oref (transient-prefix-object) level)) - 'face 'transient-higher-level))) - ("Show all suffix levels"))) - :inapt-if (lambda () (= transient-default-level transient--max-level)) - :transient t - (interactive) - (setq transient--all-levels-p (not transient--all-levels-p)) - (setq transient--refreshp t)) - -;;;; Value - -(defun transient-set () - "Set active transient's value for this Emacs session." - (interactive) - (transient-set-value (transient-prefix-object))) - -(defalias 'transient-set-and-exit #'transient-set - "Set active transient's value for this Emacs session and exit.") - -(defun transient-save () - "Save active transient's value for this and future Emacs sessions." - (interactive) - (transient-save-value (transient-prefix-object))) - -(defalias 'transient-save-and-exit #'transient-save - "Save active transient's value for this and future Emacs sessions and exit.") - -(defun transient-reset () - "Clear the set and saved values of the active transient." - (interactive) - (transient-reset-value (transient-prefix-object))) - -(defun transient-history-next () - "Switch to the next value used for the active transient." - (interactive) - (let* ((obj transient--prefix) - (pos (1- (oref obj history-pos))) - (hst (oref obj history))) - (if (< pos 0) - (user-error "End of history") - (oset obj history-pos pos) - (oset obj value (nth pos hst)) - (mapc #'transient-init-value transient--suffixes)))) - -(defun transient-history-prev () - "Switch to the previous value used for the active transient." - (interactive) - (let* ((obj transient--prefix) - (pos (1+ (oref obj history-pos))) - (hst (oref obj history)) - (len (length hst))) - (if (> pos (1- len)) - (user-error "End of history") - (oset obj history-pos pos) - (oset obj value (nth pos hst)) - (mapc #'transient-init-value transient--suffixes)))) - -;;;; Auxiliary - -(defun transient-toggle-common () - "Toggle whether common commands are permanently shown." - (interactive) - (setq transient-show-common-commands (not transient-show-common-commands))) - -(defun transient-toggle-debug () - "Toggle debugging statements for transient commands." - (interactive) - (setq transient--debug (not transient--debug)) - (message "Debugging transient %s" - (if transient--debug "enabled" "disabled"))) - -(transient-define-suffix transient-echo-arguments (arguments) - "Show the transient's active ARGUMENTS in the echo area. -Intended for use in prefixes used for demonstration purposes, -such as when suggesting a new feature or reporting an issue." - :transient t - :description "Echo arguments" - :key "x" - (interactive (list (transient-args transient-current-command))) - (message "%s: %s" - (key-description (this-command-keys)) - (mapconcat (lambda (arg) - (propertize (if (string-match-p " " arg) - (format "%S" arg) - arg) - 'face 'transient-argument)) - arguments " "))) - -;;; Value -;;;; Init - -(cl-defgeneric transient-init-scope (obj) - "Set the scope of the suffix object OBJ. - -The scope is actually a property of the transient prefix, not of -individual suffixes. However it is possible to invoke a suffix -command directly instead of from a transient. In that case, if -the suffix expects a scope, then it has to determine that itself -and store it in its `scope' slot. - -This function is called for all suffix commands, but unless a -concrete method is implemented this falls through to the default -implementation, which is a noop.") - -(cl-defmethod transient-init-scope ((_ transient-suffix)) - "Noop." nil) - -(cl-defgeneric transient-init-value (_) - "Set the initial value of the object OBJ. - -This function is called for all prefix and suffix commands. - -For suffix commands (including infix argument commands) the -default implementation is a noop. Classes derived from the -abstract `transient-infix' class must implement this function. -Non-infix suffix commands usually don't have a value." - nil) - -(cl-defmethod transient-init-value :around ((obj transient-prefix)) - "If bound, then call OBJ's `init-value' function. -Otherwise call the primary method according to object's class." - (if (slot-boundp obj 'init-value) - (funcall (oref obj init-value) obj) - (cl-call-next-method obj))) - -(cl-defmethod transient-init-value :around ((obj transient-infix)) - "If bound, then call OBJ's `init-value' function. -Otherwise call the primary method according to object's class." - (if (slot-boundp obj 'init-value) - (funcall (oref obj init-value) obj) - (cl-call-next-method obj))) - -(cl-defmethod transient-init-value ((obj transient-prefix)) - (if (slot-boundp obj 'value) - (oref obj value) - (oset obj value - (if-let ((saved (assq (oref obj command) transient-values))) - (cdr saved) - (transient-default-value obj))))) - -(cl-defmethod transient-init-value ((obj transient-argument)) - (oset obj value - (let ((value (oref transient--prefix value)) - (argument (and (slot-boundp obj 'argument) - (oref obj argument))) - (multi-value (oref obj multi-value)) - (case-fold-search nil) - (regexp (if (slot-exists-p obj 'argument-regexp) - (oref obj argument-regexp) - (format "\\`%s\\(.*\\)" (oref obj argument))))) - (if (memq multi-value '(t rest)) - (cdr (assoc argument value)) - (let ((match (lambda (v) - (and (stringp v) - (string-match regexp v) - (match-string 1 v))))) - (if multi-value - (delq nil (mapcar match value)) - (cl-some match value))))))) - -(cl-defmethod transient-init-value ((obj transient-switch)) - (oset obj value - (car (member (oref obj argument) - (oref transient--prefix value))))) - -;;;; Default - -(cl-defgeneric transient-default-value (_) - "Return the default value." - nil) - -(cl-defmethod transient-default-value ((obj transient-prefix)) - (if-let ((default (and (slot-boundp obj 'default-value) - (oref obj default-value)))) - (if (functionp default) - (funcall default) - default) - nil)) - -;;;; Read - -(cl-defgeneric transient-infix-read (obj) - "Determine the new value of the infix object OBJ. - -This function merely determines the value; `transient-infix-set' -is used to actually store the new value in the object. - -For most infix classes this is done by reading a value from the -user using the reader specified by the `reader' slot (using the -`transient-infix' method described below). - -For some infix classes the value is changed without reading -anything in the minibuffer, i.e., the mere act of invoking the -infix command determines what the new value should be, based -on the previous value.") - -(cl-defmethod transient-infix-read :around ((obj transient-infix)) - "Refresh the transient buffer and call the next method. - -Also wrap `cl-call-next-method' with two macros: -- `transient--with-suspended-override' allows use of minibuffer. -- `transient--with-emergency-exit' arranges for the transient to - be exited in case of an error." - (transient--show) - (transient--with-emergency-exit :infix-read - (transient--with-suspended-override - (cl-call-next-method obj)))) - -(cl-defmethod transient-infix-read ((obj transient-infix)) - "Read a value while taking care of history. - -This method is suitable for a wide variety of infix commands, -including but not limited to inline arguments and variables. - -If you do not use this method for your own infix class, then -you should likely replicate a lot of the behavior of this -method. If you fail to do so, then users might not appreciate -the lack of history, for example. - -Only for very simple classes that toggle or cycle through a very -limited number of possible values should you replace this with a -simple method that does not handle history. (E.g., for a command -line switch the only possible values are \"use it\" and \"don't use -it\", in which case it is pointless to preserve history.)" - (with-slots (value multi-value always-read allow-empty choices) obj - (if (and value - (not multi-value) - (not always-read) - transient--prefix) - (oset obj value nil) - (let* ((enable-recursive-minibuffers t) - (reader (oref obj reader)) - (choices (if (functionp choices) (funcall choices) choices)) - (prompt (transient-prompt obj)) - (value (if multi-value (mapconcat #'identity value ",") value)) - (history-key (or (oref obj history-key) - (oref obj command))) - (transient--history (alist-get history-key transient-history)) - (transient--history (if (or (null value) - (eq value (car transient--history))) - transient--history - (cons value transient--history))) - (initial-input (and transient-read-with-initial-input - (car transient--history))) - (history (if initial-input - (cons 'transient--history 1) - 'transient--history)) - (value - (cond - (reader (funcall reader prompt initial-input history)) - (multi-value - (completing-read-multiple prompt choices nil nil - initial-input history)) - (choices - (completing-read prompt choices nil t initial-input history)) - ((read-string prompt initial-input history))))) - (cond ((and (equal value "") (not allow-empty)) - (setq value nil)) - ((and (equal value "\"\"") allow-empty) - (setq value ""))) - (when value - (when (and (bound-and-true-p ivy-mode) - (stringp (car transient--history))) - (set-text-properties 0 (length (car transient--history)) nil - (car transient--history))) - (setf (alist-get history-key transient-history) - (delete-dups transient--history))) - value)))) - -(cl-defmethod transient-infix-read ((obj transient-switch)) - "Toggle the switch on or off." - (if (oref obj value) nil (oref obj argument))) - -(cl-defmethod transient-infix-read ((obj transient-switches)) - "Cycle through the mutually exclusive switches. -The last value is \"don't use any of these switches\"." - (let ((choices (mapcar (apply-partially #'format (oref obj argument-format)) - (oref obj choices)))) - (if-let ((value (oref obj value))) - (cadr (member value choices)) - (car choices)))) - -(cl-defmethod transient-infix-read ((command symbol)) - "Elsewhere use the reader of the infix command COMMAND. -Use this if you want to share an infix's history with a regular -stand-alone command." - (if-let ((obj (transient--suffix-prototype command))) - (cl-letf (((symbol-function #'transient--show) #'ignore)) - (transient-infix-read obj)) - (error "Not a suffix command: `%s'" command))) - -;;;; Readers - -(defun transient-read-file (prompt _initial-input _history) - "Read a file." - (file-local-name (expand-file-name (read-file-name prompt)))) - -(defun transient-read-existing-file (prompt _initial-input _history) - "Read an existing file." - (file-local-name (expand-file-name (read-file-name prompt nil nil t)))) - -(defun transient-read-directory (prompt _initial-input _history) - "Read a directory." - (file-local-name (expand-file-name (read-directory-name prompt)))) - -(defun transient-read-existing-directory (prompt _initial-input _history) - "Read an existing directory." - (file-local-name (expand-file-name (read-directory-name prompt nil nil t)))) - -(defun transient-read-number-N0 (prompt initial-input history) - "Read a natural number (including zero) and return it as a string." - (transient--read-number-N prompt initial-input history t)) - -(defun transient-read-number-N+ (prompt initial-input history) - "Read a natural number (excluding zero) and return it as a string." - (transient--read-number-N prompt initial-input history nil)) - -(defun transient--read-number-N (prompt initial-input history include-zero) - (save-match-data - (cl-block nil - (while t - (let ((str (read-from-minibuffer prompt initial-input nil nil history))) - (when (or (string-equal str "") - (string-match-p (if include-zero - "\\`\\(0\\|[1-9][0-9]*\\)\\'" - "\\`[1-9][0-9]*\\'") - str)) - (cl-return str))) - (message "Please enter a natural number (%s zero)." - (if include-zero "including" "excluding")) - (sit-for 1))))) - -(defun transient-read-date (prompt default-time _history) - "Read a date using `org-read-date' (which see)." - (require 'org) - (when (fboundp 'org-read-date) - (org-read-date 'with-time nil nil prompt default-time))) - -;;;; Prompt - -(cl-defgeneric transient-prompt (obj) - "Return the prompt to be used to read infix object OBJ's value.") - -(cl-defmethod transient-prompt ((obj transient-infix)) - "Return the prompt to be used to read infix object OBJ's value. - -This implementation should be suitable for almost all infix -commands. - -If the value of OBJ's `prompt' slot is non-nil, then it must be -a string or a function. If it is a string, then use that. If -it is a function, then call that with OBJ as the only argument. -That function must return a string, which is then used as the -prompt. - -Otherwise, if the value of either the `argument' or `variable' -slot of OBJ is a string, then base the prompt on that (preferring -the former), appending either \"=\" (if it appears to be a -command-line option) or \": \". - -Finally fall through to using \"(BUG: no prompt): \" as the -prompt." - (if-let ((prompt (oref obj prompt))) - (let ((prompt (if (functionp prompt) - (funcall prompt obj) - prompt))) - (if (stringp prompt) - prompt - "(BUG: no prompt): ")) - (or (and-let* ((arg (and (slot-boundp obj 'argument) (oref obj argument)))) - (if (and (stringp arg) (string-suffix-p "=" arg)) - arg - (concat arg ": "))) - (and-let* ((var (and (slot-boundp obj 'variable) (oref obj variable)))) - (and (stringp var) - (concat var ": "))) - "(BUG: no prompt): "))) - -;;;; Set - -(cl-defgeneric transient-infix-set (obj value) - "Set the value of infix object OBJ to value.") - -(cl-defmethod transient-infix-set ((obj transient-infix) value) - "Set the value of infix object OBJ to value." - (oset obj value value)) - -(cl-defmethod transient-infix-set :after ((obj transient-argument) value) - "Unset incompatible infix arguments." - (when-let* ((value) - (val (transient-infix-value obj)) - (arg (if (slot-boundp obj 'argument) - (oref obj argument) - (oref obj argument-format))) - (spec (oref transient--prefix incompatible)) - (filter (lambda (x rule) - (and (member x rule) - (remove x rule)))) - (incomp (nconc - (cl-mapcan (apply-partially filter arg) spec) - (and (not (equal val arg)) - (cl-mapcan (apply-partially filter val) spec))))) - (dolist (obj transient--suffixes) - (when-let* (((cl-typep obj 'transient-argument)) - (val (transient-infix-value obj)) - (arg (if (slot-boundp obj 'argument) - (oref obj argument) - (oref obj argument-format))) - ((if (equal val arg) - (member arg incomp) - (or (member val incomp) - (member arg incomp))))) - (transient-infix-set obj nil))))) - -(cl-defgeneric transient-set-value (obj) - "Set the value of the transient prefix OBJ.") - -(cl-defmethod transient-set-value ((obj transient-prefix)) - (oset (oref obj prototype) value (transient-get-value)) - (transient--history-push obj)) - -;;;; Save - -(cl-defgeneric transient-save-value (obj) - "Save the value of the transient prefix OBJ.") - -(cl-defmethod transient-save-value ((obj transient-prefix)) - (let ((value (transient-get-value))) - (oset (oref obj prototype) value value) - (setf (alist-get (oref obj command) transient-values) value) - (transient-save-values)) - (transient--history-push obj)) - -;;;; Reset - -(cl-defgeneric transient-reset-value (obj) - "Clear the set and saved values of the transient prefix OBJ.") - -(cl-defmethod transient-reset-value ((obj transient-prefix)) - (let ((value (transient-default-value obj))) - (oset obj value value) - (oset (oref obj prototype) value value) - (setf (alist-get (oref obj command) transient-values nil 'remove) nil) - (transient-save-values)) - (transient--history-push obj) - (mapc #'transient-init-value transient--suffixes)) - -;;;; Get - -(defun transient-args (prefix) - "Return the value of the transient prefix command PREFIX. -If the current command was invoked from the transient prefix -command PREFIX, then return the active infix arguments. If -the current command was not invoked from PREFIX, then return -the set, saved or default value for PREFIX." - (cl-mapcan #'transient--get-wrapped-value (transient-suffixes prefix))) - -(defun transient-suffixes (prefix) - "Return the suffix objects of the transient prefix command PREFIX." - (if (eq transient-current-command prefix) - transient-current-suffixes - (let ((transient--prefix (transient--init-prefix prefix))) - (transient--flatten-suffixes - (transient--init-suffixes prefix))))) - -(defun transient-get-value () - (transient--with-emergency-exit :get-value - (cl-mapcan (lambda (obj) - (and (or (not (slot-exists-p obj 'unsavable)) - (not (oref obj unsavable))) - (transient--get-wrapped-value obj))) - transient-current-suffixes))) - -(defun transient--get-wrapped-value (obj) - (and-let* ((value (transient-infix-value obj))) - (pcase-exhaustive (and (slot-exists-p obj 'multi-value) - (oref obj multi-value)) - ('nil (list value)) - ((or 't 'rest) (list value)) - ('repeat value)))) - -(cl-defgeneric transient-infix-value (obj) - "Return the value of the suffix object OBJ. - -This function is called by `transient-args' (which see), meaning -this function is how the value of a transient is determined so -that the invoked suffix command can use it. - -Currently most values are strings, but that is not set in stone. -Nil is not a value, it means \"no value\". - -Usually only infixes have a value, but see the method for -`transient-suffix'.") - -(cl-defmethod transient-infix-value ((_ transient-suffix)) - "Return nil, which means \"no value\". - -Infix arguments contribute the transient's value while suffix -commands consume it. This function is called for suffixes anyway -because a command that both contributes to the transient's value -and also consumes it is not completely unconceivable. - -If you define such a command, then you must define a derived -class and implement this function because this default method -does nothing." nil) - -(cl-defmethod transient-infix-value ((obj transient-infix)) - "Return the value of OBJ's `value' slot." - (oref obj value)) - -(cl-defmethod transient-infix-value ((obj transient-option)) - "Return ARGUMENT and VALUE as a unit or nil if the latter is nil." - (and-let* ((value (oref obj value))) - (let ((arg (oref obj argument))) - (pcase-exhaustive (oref obj multi-value) - ('nil (concat arg value)) - ((or 't 'rest) (cons arg value)) - ('repeat (mapcar (lambda (v) (concat arg v)) value)))))) - -(cl-defmethod transient-infix-value ((_ transient-variable)) - "Return nil, which means \"no value\". - -Setting the value of a variable is done by, well, setting the -value of the variable. I.e., this is a side-effect and does -not contribute to the value of the transient." - nil) - -;;;; Utilities - -(defun transient-arg-value (arg args) - "Return the value of ARG as it appears in ARGS. - -For a switch return a boolean. For an option return the value as -a string, using the empty string for the empty value, or nil if -the option does not appear in ARGS." - (if (string-suffix-p "=" arg) - (save-match-data - (and-let* ((match (let ((case-fold-search nil) - (re (format "\\`%s\\(?:=\\(.+\\)\\)?\\'" - (substring arg 0 -1)))) - (cl-find-if (lambda (a) - (and (stringp a) - (string-match re a))) - args)))) - (or (match-string 1 match) ""))) - (and (member arg args) t))) - -(defun transient-scope () - "Return the value of the `scope' slot of the current prefix." - (oref (transient-prefix-object) scope)) - -;;; History - -(cl-defgeneric transient--history-key (obj) - "Return OBJ's history key. -If the value of the `history-key' slot is non-nil, then return -that. Otherwise return the value of the `command' slot." - (or (oref obj history-key) - (oref obj command))) - -(cl-defgeneric transient--history-push (obj) - "Push the current value of OBJ to its entry in `transient-history'." - (let ((key (transient--history-key obj))) - (setf (alist-get key transient-history) - (let ((args (transient-get-value))) - (cons args (delete args (alist-get key transient-history))))))) - -(cl-defgeneric transient--history-init (obj) - "Initialize OBJ's `history' slot. -This is the transient-wide history; many individual infixes also -have a history of their own.") - -(cl-defmethod transient--history-init ((obj transient-prefix)) - "Initialize OBJ's `history' slot from the variable `transient-history'." - (let ((val (oref obj value))) - (oset obj history - (cons val (delete val (alist-get (transient--history-key obj) - transient-history)))))) - -;;; Draw - -(defun transient--show-brief () - (let ((message-log-max nil)) - (if (and transient-show-popup (<= transient-show-popup 0)) - (message "%s-" (key-description (this-command-keys))) - (message - "%s- [%s] %s" - (key-description (this-command-keys)) - (oref transient--prefix command) - (mapconcat - #'identity - (sort - (cl-mapcan - (lambda (suffix) - (let ((key (kbd (oref suffix key)))) - ;; Don't list any common commands. - (and (not (memq (oref suffix command) - `(,(lookup-key transient-map key) - ,(lookup-key transient-sticky-map key) - ;; From transient-common-commands: - transient-set - transient-save - transient-history-prev - transient-history-next - transient-quit-one - transient-toggle-common - transient-set-level))) - (list (propertize (oref suffix key) 'face 'transient-key))))) - transient--suffixes) - #'string<) - (propertize "|" 'face 'transient-delimiter)))))) - -(defun transient--show () - (transient--timer-cancel) - (setq transient--showp t) - (let ((transient--shadowed-buffer (current-buffer)) - (focus nil)) - (setq transient--buffer (get-buffer-create transient--buffer-name)) - (with-current-buffer transient--buffer - (when transient-enable-popup-navigation - (setq focus (or (button-get (point) 'command) - (and (not (bobp)) - (button-get (1- (point)) 'command)) - (transient--heading-at-point)))) - (erase-buffer) - (run-hooks 'transient-setup-buffer-hook) - (when transient-force-fixed-pitch - (transient--force-fixed-pitch)) - (setq window-size-fixed t) - (when (bound-and-true-p tab-line-format) - (setq tab-line-format nil)) - (setq header-line-format nil) - (setq mode-line-format - (if (or (natnump transient-mode-line-format) - (eq transient-mode-line-format 'line)) - nil - transient-mode-line-format)) - (setq mode-line-buffer-identification - (symbol-name (oref transient--prefix command))) - (if transient-enable-popup-navigation - (setq-local cursor-in-non-selected-windows 'box) - (setq cursor-type nil)) - (setq display-line-numbers nil) - (setq show-trailing-whitespace nil) - (transient--insert-groups) - (when (or transient--helpp transient--editp) - (transient--insert-help)) - (when-let ((line (transient--separator-line))) - (insert line))) - (unless (window-live-p transient--window) - (setq transient--window - (display-buffer transient--buffer - transient-display-buffer-action))) - (when (window-live-p transient--window) - (with-selected-window transient--window - (goto-char (point-min)) - (when transient-enable-popup-navigation - (transient--goto-button focus)) - (transient--fit-window-to-buffer transient--window))))) - -(defun transient--fit-window-to-buffer (window) - (let ((window-resize-pixelwise t) - (window-size-fixed nil)) - (if (eq (car (window-parameter window 'quit-restore)) 'other) - ;; Grow but never shrink window that previously displayed - ;; another buffer and is going to display that again. - (fit-window-to-buffer window nil (window-height window)) - (fit-window-to-buffer window nil 1)))) - -(defun transient--separator-line () - (and-let* ((height (cond ((not window-system) nil) - ((natnump transient-mode-line-format) - transient-mode-line-format) - ((eq transient-mode-line-format 'line) 1))) - (face `(,@(and (>= emacs-major-version 27) '(:extend t)) - :background - ,(or (face-foreground (transient--key-face nil 'non-suffix) - nil t) - "#gray60")))) - (concat (propertize "__" 'face face 'display `(space :height (,height))) - (propertize "\n" 'face face 'line-height t)))) - -(defmacro transient-with-shadowed-buffer (&rest body) - "While in the transient buffer, temporarly make the shadowed buffer current." - (declare (indent 0) (debug t)) - `(with-current-buffer (or transient--shadowed-buffer (current-buffer)) - ,@body)) - -(defun transient--insert-groups () - (let ((groups (cl-mapcan (lambda (group) - (let ((hide (oref group hide))) - (and (not (and (functionp hide) - (transient-with-shadowed-buffer - (funcall hide)))) - (list group)))) - transient--layout))) - (while-let ((group (pop groups))) - (transient--insert-group group) - (when groups - (insert ?\n))))) - -(defvar transient--max-group-level 1) - -(cl-defgeneric transient--insert-group (group) - "Format GROUP and its elements and insert the result.") - -(cl-defmethod transient--insert-group :around ((group transient-group)) - "Insert GROUP's description, if any." - (when-let ((desc (transient-with-shadowed-buffer - (transient-format-description group)))) - (insert desc ?\n)) - (let ((transient--max-group-level - (max (oref group level) transient--max-group-level)) - (transient--pending-group group)) - (cl-call-next-method group))) - -(cl-defmethod transient--insert-group ((group transient-row)) - (transient--maybe-pad-keys group) - (dolist (suffix (oref group suffixes)) - (insert (transient-with-shadowed-buffer (transient-format suffix))) - (insert " ")) - (insert ?\n)) - -(cl-defmethod transient--insert-group ((group transient-column)) - (transient--maybe-pad-keys group) - (dolist (suffix (oref group suffixes)) - (let ((str (transient-with-shadowed-buffer (transient-format suffix)))) - (insert str) - (unless (string-match-p ".\n\\'" str) - (insert ?\n))))) - -(cl-defmethod transient--insert-group ((group transient-columns)) - (let* ((columns - (mapcar - (lambda (column) - (transient--maybe-pad-keys column group) - (transient-with-shadowed-buffer - (let* ((transient--pending-group column) - (rows (mapcar #'transient-format (oref column suffixes)))) - (when-let ((desc (transient-format-description column))) - (push desc rows)) - (flatten-tree rows)))) - (oref group suffixes))) - (vp (or (oref transient--prefix variable-pitch) - transient-align-variable-pitch)) - (rs (apply #'max (mapcar #'length columns))) - (cs (length columns)) - (cw (mapcar (let ((widths (oref transient--prefix column-widths))) - (lambda (col) - (apply - #'max - (if-let ((min (pop widths))) - (if vp (* min (transient--pixel-width " ")) min) - 0) - (mapcar (if vp #'transient--pixel-width #'length) - col)))) - columns)) - (cc (transient--seq-reductions-from - (apply-partially #'+ (* 3 (if vp (transient--pixel-width " ") 1))) - cw 0))) - (if transient-force-single-column - (dotimes (c cs) - (dotimes (r rs) - (when-let ((cell (nth r (nth c columns)))) - (unless (equal cell "") - (insert cell ?\n)))) - (unless (= c (1- cs)) - (insert ?\n))) - (dotimes (r rs) - (dotimes (c cs) - (if vp - (progn - (when-let ((cell (nth r (nth c columns)))) - (insert cell)) - (if (= c (1- cs)) - (insert ?\n) - (insert (propertize " " 'display - `(space :align-to (,(nth (1+ c) cc))))))) - (when (> c 0) - (insert (make-string (max 1 (- (nth c cc) (current-column))) - ?\s))) - (when-let ((cell (nth r (nth c columns)))) - (insert cell)) - (when (= c (1- cs)) - (insert ?\n)))))))) - -(cl-defmethod transient--insert-group ((group transient-subgroups)) - (let ((subgroups (oref group suffixes))) - (while-let ((subgroup (pop subgroups))) - (transient--maybe-pad-keys subgroup group) - (transient--insert-group subgroup) - (when subgroups - (insert ?\n))))) - -(cl-defgeneric transient-format (obj) - "Format and return OBJ for display. - -When this function is called, then the current buffer is some -temporary buffer. If you need the buffer from which the prefix -command was invoked to be current, then do so by temporarily -making `transient--original-buffer' current.") - -(cl-defmethod transient-format ((arg string)) - "Return the string ARG after applying the `transient-heading' face." - (propertize arg 'face 'transient-heading)) - -(cl-defmethod transient-format ((_ null)) - "Return a string containing just the newline character." - "\n") - -(cl-defmethod transient-format ((arg integer)) - "Return a string containing just the ARG character." - (char-to-string arg)) - -(cl-defmethod transient-format :around ((obj transient-suffix)) - "Add additional formatting if appropriate. -When reading user input for this infix, then highlight it. -When edit-mode is enabled, then prepend the level information. -When `transient-enable-popup-navigation' is non-nil then format -as a button." - (let ((str (cl-call-next-method obj))) - (when (and (cl-typep obj 'transient-infix) - (eq (oref obj command) this-original-command) - (active-minibuffer-window)) - (setq str (transient--add-face str 'transient-active-infix))) - (when transient--editp - (setq str (concat (let ((level (oref obj level))) - (propertize (format " %s " level) - 'face (if (transient--use-level-p level t) - 'transient-enabled-suffix - 'transient-disabled-suffix))) - str))) - (when (and transient-enable-popup-navigation - (slot-boundp obj 'command)) - (setq str (make-text-button str nil - 'type 'transient - 'command (oref obj command)))) - str)) - -(cl-defmethod transient-format ((obj transient-infix)) - "Return a string generated using OBJ's `format'. -%k is formatted using `transient-format-key'. -%d is formatted using `transient-format-description'. -%v is formatted using `transient-format-value'." - (format-spec (oref obj format) - `((?k . ,(transient-format-key obj)) - (?d . ,(transient-format-description obj)) - (?v . ,(transient-format-value obj))))) - -(cl-defmethod transient-format ((obj transient-suffix)) - "Return a string generated using OBJ's `format'. -%k is formatted using `transient-format-key'. -%d is formatted using `transient-format-description'." - (format-spec (oref obj format) - `((?k . ,(transient-format-key obj)) - (?d . ,(transient-format-description obj))))) - -(cl-defgeneric transient-format-key (obj) - "Format OBJ's `key' for display and return the result.") - -(cl-defmethod transient-format-key :around ((obj transient-suffix)) - "Add `transient-inapt-suffix' face if suffix is inapt." - (let ((str (cl-call-next-method))) - (if (oref obj inapt) - (transient--add-face str 'transient-inapt-suffix) - str))) - -(cl-defmethod transient-format-key ((obj transient-suffix)) - "Format OBJ's `key' for display and return the result." - (let ((key (if (slot-boundp obj 'key) (oref obj key) "")) - (cmd (and (slot-boundp obj 'command) (oref obj command)))) - (when-let ((width (oref transient--pending-group pad-keys))) - (setq key (truncate-string-to-width key width nil ?\s))) - (if transient--redisplay-key - (let ((len (length transient--redisplay-key)) - (seq (cl-coerce (edmacro-parse-keys key t) 'list))) - (cond - ((member (seq-take seq len) - (list transient--redisplay-key - (thread-last transient--redisplay-key - (cl-substitute ?- 'kp-subtract) - (cl-substitute ?= 'kp-equal) - (cl-substitute ?+ 'kp-add)))) - (let ((pre (key-description (vconcat (seq-take seq len)))) - (suf (key-description (vconcat (seq-drop seq len))))) - (setq pre (string-replace "RET" "C-m" pre)) - (setq pre (string-replace "TAB" "C-i" pre)) - (setq suf (string-replace "RET" "C-m" suf)) - (setq suf (string-replace "TAB" "C-i" suf)) - ;; We use e.g., "-k" instead of the more correct "- k", - ;; because the former is prettier. If we did that in - ;; the definition, then we want to drop the space that - ;; is reinserted above. False-positives are possible - ;; for silly bindings like "-C-c C-c". - (unless (string-search " " key) - (setq pre (string-replace " " "" pre)) - (setq suf (string-replace " " "" suf))) - (concat (propertize pre 'face 'transient-unreachable-key) - (and (string-prefix-p (concat pre " ") key) " ") - (propertize suf 'face (transient--key-face cmd)) - (save-excursion - (and (string-match " +\\'" key) - (propertize (match-string 0 key) - 'face 'fixed-pitch)))))) - ((transient--lookup-key transient-sticky-map (kbd key)) - (propertize key 'face (transient--key-face cmd))) - (t - (propertize key 'face 'transient-unreachable-key)))) - (propertize key 'face (transient--key-face cmd))))) - -(cl-defmethod transient-format-key :around ((obj transient-argument)) - "Handle `transient-highlight-mismatched-keys'." - (let ((key (cl-call-next-method obj))) - (cond - ((not transient-highlight-mismatched-keys) key) - ((not (slot-boundp obj 'shortarg)) - (transient--add-face key 'transient-nonstandard-key)) - ((not (string-equal key (oref obj shortarg))) - (transient--add-face key 'transient-mismatched-key)) - (key)))) - -(cl-defgeneric transient-format-description (obj) - "Format OBJ's `description' for display and return the result.") - -(cl-defmethod transient-format-description ((obj transient-suffix)) - "The `description' slot may be a function, in which case that is -called inside the correct buffer (see `transient--insert-group') -and its value is returned to the caller." - (transient--get-description obj)) - -(cl-defmethod transient-format-description ((obj transient-group)) - "Format the description by calling the next method. If the result -doesn't use the `face' property at all, then apply the face -`transient-heading' to the complete string." - (and-let* ((desc (transient--get-description obj))) - (cond ((oref obj inapt) - (propertize desc 'face 'transient-inapt-suffix)) - ((text-property-not-all 0 (length desc) 'face nil desc) - desc) - ((propertize desc 'face 'transient-heading))))) - -(cl-defmethod transient-format-description :around ((obj transient-suffix)) - "Format the description by calling the next method. If the result -is nil, then use \"(BUG: no description)\" as the description. -If the OBJ's `key' is currently unreachable, then apply the face -`transient-unreachable' to the complete string." - (let ((desc (or (cl-call-next-method obj) - (and (slot-boundp transient--prefix 'suffix-description) - (funcall (oref transient--prefix suffix-description) - obj))))) - (if desc - (when-let ((face (transient--get-face obj 'face))) - (setq desc (transient--add-face desc face t))) - (setq desc (propertize "(BUG: no description)" 'face 'error))) - (when (if transient--all-levels-p - (> (oref obj level) transient--default-prefix-level) - (and transient-highlight-higher-levels - (> (max (oref obj level) transient--max-group-level) - transient--default-prefix-level))) - (setq desc (transient--add-face desc 'transient-higher-level))) - (when-let ((inapt-face (and (oref obj inapt) - (transient--get-face obj 'inapt-face)))) - (setq desc (transient--add-face desc inapt-face))) - (when (and (slot-boundp obj 'key) - (transient--key-unreachable-p obj)) - (setq desc (transient--add-face desc 'transient-unreachable))) - desc)) - -(cl-defgeneric transient-format-value (obj) - "Format OBJ's value for display and return the result.") - -(cl-defmethod transient-format-value ((obj transient-suffix)) - (propertize (oref obj argument) - 'face (if (oref obj value) - 'transient-argument - 'transient-inactive-argument))) - -(cl-defmethod transient-format-value ((obj transient-option)) - (let ((argument (oref obj argument))) - (if-let ((value (oref obj value))) - (pcase-exhaustive (oref obj multi-value) - ('nil - (concat (propertize argument 'face 'transient-argument) - (propertize value 'face 'transient-value))) - ((or 't 'rest) - (concat (propertize (if (string-suffix-p " " argument) - argument - (concat argument " ")) - 'face 'transient-argument) - (propertize (mapconcat #'prin1-to-string value " ") - 'face 'transient-value))) - ('repeat - (mapconcat (lambda (value) - (concat (propertize argument 'face 'transient-argument) - (propertize value 'face 'transient-value))) - value " "))) - (propertize argument 'face 'transient-inactive-argument)))) - -(cl-defmethod transient-format-value ((obj transient-switches)) - (with-slots (value argument-format choices) obj - (format (propertize argument-format - 'face (if value - 'transient-argument - 'transient-inactive-argument)) - (format - (propertize "[%s]" 'face 'transient-delimiter) - (mapconcat - (lambda (choice) - (propertize choice 'face - (if (equal (format argument-format choice) value) - 'transient-value - 'transient-inactive-value))) - choices - (propertize "|" 'face 'transient-delimiter)))))) - -(cl-defmethod transient--get-description ((obj transient-child)) - (and-let* ((desc (oref obj description))) - (if (functionp desc) - (if (= (car (transient--func-arity desc)) 1) - (funcall desc obj) - (funcall desc)) - desc))) - -(cl-defmethod transient--get-face ((obj transient-suffix) slot) - (and-let* (((slot-boundp obj slot)) - (face (slot-value obj slot))) - (if (and (not (facep face)) - (functionp face)) - (let ((transient--pending-suffix obj)) - (if (= (car (transient--func-arity face)) 1) - (funcall face obj) - (funcall face))) - face))) - -(defun transient--add-face (string face &optional append beg end) - (let ((str (copy-sequence string))) - (add-face-text-property (or beg 0) (or end (length str)) face append str) - str)) - -(defun transient--key-face (&optional cmd enforce-type) - (or (and transient-semantic-coloring - (not transient--helpp) - (not transient--editp) - (or (and cmd (get cmd 'transient-face)) - (get (transient--get-pre-command cmd enforce-type) - 'transient-face))) - (if cmd 'transient-key 'transient-key-noop))) - -(defun transient--key-unreachable-p (obj) - (and transient--redisplay-key - (let ((key (oref obj key))) - (not (or (equal (seq-take (cl-coerce (edmacro-parse-keys key t) 'list) - (length transient--redisplay-key)) - transient--redisplay-key) - (transient--lookup-key transient-sticky-map (kbd key))))))) - -(defun transient--lookup-key (keymap key) - (let ((val (lookup-key keymap key))) - (and val (not (integerp val)) val))) - -(defun transient--maybe-pad-keys (group &optional parent) - (when-let ((pad (or (oref group pad-keys) - (and parent (oref parent pad-keys))))) - (oset group pad-keys - (apply #'max - (if (integerp pad) pad 0) - (seq-keep (lambda (suffix) - (and (eieio-object-p suffix) - (slot-boundp suffix 'key) - (length (oref suffix key)))) - (oref group suffixes)))))) - -(defun transient--pixel-width (string) - (save-window-excursion - (with-temp-buffer - (insert string) - (set-window-dedicated-p nil nil) - (set-window-buffer nil (current-buffer)) - (car (window-text-pixel-size - nil (line-beginning-position) (point)))))) - -(defun transient-command-summary-or-name (obj) - "Return the summary or name of the command represented by OBJ. - -If the command has a doc-string, then return the first line of -that, else its name. - -Intended to be temporarily used as the `:suffix-description' of -a prefix command, while porting a regular keymap to a transient." - (let ((command (oref obj command))) - (if-let ((doc (documentation command))) - (propertize (car (split-string doc "\n")) 'face 'font-lock-doc-face) - (propertize (symbol-name command) 'face 'font-lock-function-name-face)))) - -;;; Help - -(cl-defgeneric transient-show-help (obj) - "Show documentation for the command represented by OBJ.") - -(cl-defmethod transient-show-help ((obj transient-prefix)) - "Call `show-help' if non-nil, else show `info-manual', -if non-nil, else show the `man-page' if non-nil, else use -`describe-function'." - (with-slots (show-help info-manual man-page command) obj - (cond (show-help (funcall show-help obj)) - (info-manual (transient--show-manual info-manual)) - (man-page (transient--show-manpage man-page)) - ((transient--describe-function command))))) - -(cl-defmethod transient-show-help ((obj transient-suffix)) - "Call `show-help' if non-nil, else use `describe-function'. -Also used to dispatch showing documentation for the current -prefix. If the suffix is a sub-prefix, then also call the -prefix method." - (cond - ((eq this-command 'transient-help) - (transient-show-help transient--prefix)) - ((let ((prefix (get (oref obj command) - 'transient--prefix))) - (and prefix (not (eq (oref transient--prefix command) this-command)) - (prog1 t (transient-show-help prefix))))) - ((if-let ((show-help (oref obj show-help))) - (funcall show-help obj) - (transient--describe-function this-command))))) - -(cl-defmethod transient-show-help ((obj transient-infix)) - "Call `show-help' if non-nil, else show the `man-page' -if non-nil, else use `describe-function'. When showing the -manpage, then try to jump to the correct location." - (if-let ((show-help (oref obj show-help))) - (funcall show-help obj) - (if-let ((man-page (oref transient--prefix man-page)) - (argument (and (slot-boundp obj 'argument) - (oref obj argument)))) - (transient--show-manpage man-page argument) - (transient--describe-function this-command)))) - -;; `cl-generic-generalizers' doesn't support `command' et al. -(cl-defmethod transient-show-help (cmd) - "Show the command doc-string." - (transient--describe-function cmd)) - -(defun transient--describe-function (fn) - (describe-function fn) - (unless (derived-mode-p 'help-mode) - (when-let* ((buf (get-buffer "*Help*")) - (win (or (and buf (get-buffer-window buf)) - (cl-find-if (lambda (win) - (with-current-buffer (window-buffer win) - (derived-mode-p 'help-mode))) - (window-list))))) - (select-window win)))) - -(defun transient--show-manual (manual) - (info manual)) - -(defun transient--show-manpage (manpage &optional argument) - (require 'man) - (let* ((Man-notify-method 'meek) - (buf (Man-getpage-in-background manpage)) - (proc (get-buffer-process buf))) - (while (and proc (eq (process-status proc) 'run)) - (accept-process-output proc)) - (switch-to-buffer buf) - (when argument - (transient--goto-argument-description argument)))) - -(defun transient--goto-argument-description (arg) - (goto-char (point-min)) - (let ((case-fold-search nil) - ;; This matches preceding/proceeding options. Options - ;; such as "-a", "-S[<keyid>]", and "--grep=<pattern>" - ;; are matched by this regex without the shy group. - ;; The ". " in the shy group is for options such as - ;; "-m parent-number", and the "-[^[:space:]]+ " is - ;; for options such as "--mainline parent-number" - (others "-\\(?:. \\|-[^[:space:]]+ \\)?[^[:space:]]+")) - (when (re-search-forward - (if (equal arg "--") - ;; Special case. - "^[\t\s]+\\(--\\(?: \\|$\\)\\|\\[--\\]\\)" - ;; Should start with whitespace and may have - ;; any number of options before and/or after. - (format - "^[\t\s]+\\(?:%s, \\)*?\\(?1:%s\\)%s\\(?:, %s\\)*$" - others - ;; Options don't necessarily end in an "=" - ;; (e.g., "--gpg-sign[=<keyid>]") - (string-remove-suffix "=" arg) - ;; Simple options don't end in an "=". Splitting this - ;; into 2 cases should make getting false positives - ;; less likely. - (if (string-suffix-p "=" arg) - ;; "[^[:space:]]*[^.[:space:]]" matches the option - ;; value, which is usually after the option name - ;; and either '=' or '[='. The value can't end in - ;; a period, as that means it's being used at the - ;; end of a sentence. The space is for options - ;; such as '--mainline parent-number'. - "\\(?: \\|\\[?=\\)[^[:space:]]*[^.[:space:]]" - ;; Either this doesn't match anything (e.g., "-a"), - ;; or the option is followed by a value delimited - ;; by a "[", "<", or ":". A space might appear - ;; before this value, as in "-f <file>". The - ;; space alternative is for options such as - ;; "-m parent-number". - "\\(?:\\(?: \\| ?[\\[<:]\\)[^[:space:]]*[^.[:space:]]\\)?") - others)) - nil t) - (goto-char (match-beginning 1))))) - -(defun transient--insert-help () - (unless (looking-back "\n\n" 2) - (insert "\n")) - (when transient--helpp - (insert - (format (propertize "\ -Type a %s to show help for that suffix command, or %s to show manual. -Type %s to exit help.\n" - 'face 'transient-heading) - (propertize "<KEY>" 'face 'transient-key) - (propertize "?" 'face 'transient-key) - (propertize "C-g" 'face 'transient-key)))) - (when transient--editp - (unless transient--helpp - (insert - (format (propertize "\ -Type a %s to set level for that suffix command. -Type %s to set what levels are available for this prefix command.\n" - 'face 'transient-heading) - (propertize "<KEY>" 'face 'transient-key) - (propertize "C-x l" 'face 'transient-key)))) - (with-slots (level) transient--prefix - (insert - (format (propertize " -Suffixes on levels %s are available. -Suffixes on levels %s and %s are unavailable.\n" - 'face 'transient-heading) - (propertize (format "1-%s" level) - 'face 'transient-enabled-suffix) - (propertize " 0 " - 'face 'transient-disabled-suffix) - (propertize (format ">=%s" (1+ level)) - 'face 'transient-disabled-suffix)))))) - -;;; Popup Navigation - -(defun transient-scroll-up (&optional arg) - "Scroll text of transient popup window upward ARG lines. -If ARG is nil scroll near full screen. This is a wrapper -around `scroll-up-command' (which see)." - (interactive "^P") - (with-selected-window transient--window - (scroll-up-command arg))) - -(defun transient-scroll-down (&optional arg) - "Scroll text of transient popup window down ARG lines. -If ARG is nil scroll near full screen. This is a wrapper -around `scroll-down-command' (which see)." - (interactive "^P") - (with-selected-window transient--window - (scroll-down-command arg))) - -(defun transient-backward-button (n) - "Move to the previous button in the transient popup buffer. -See `backward-button' for information about N." - (interactive "p") - (with-selected-window transient--window - (backward-button n t))) - -(defun transient-forward-button (n) - "Move to the next button in the transient popup buffer. -See `forward-button' for information about N." - (interactive "p") - (with-selected-window transient--window - (forward-button n t))) - -(define-button-type 'transient - 'face nil - 'keymap transient-button-map) - -(defun transient--goto-button (command) - (cond - ((stringp command) - (when (re-search-forward (concat "^" (regexp-quote command)) nil t) - (goto-char (match-beginning 0)))) - (command - (while (and (ignore-errors (forward-button 1)) - (not (eq (button-get (button-at (point)) 'command) command)))) - (unless (eq (button-get (button-at (point)) 'command) command) - (goto-char (point-min)) - (forward-button 1))))) - -(defun transient--heading-at-point () - (and (eq (get-text-property (point) 'face) 'transient-heading) - (let ((beg (line-beginning-position))) - (buffer-substring-no-properties - beg (next-single-property-change - beg 'face nil (line-end-position)))))) - -;;; Compatibility -;;;; Popup Isearch - -(defvar-keymap transient--isearch-mode-map - :parent isearch-mode-map - "<remap> <isearch-exit>" #'transient-isearch-exit - "<remap> <isearch-cancel>" #'transient-isearch-cancel - "<remap> <isearch-abort>" #'transient-isearch-abort) - -(defun transient-isearch-backward (&optional regexp-p) - "Do incremental search backward. -With a prefix argument, do an incremental regular expression -search instead." - (interactive "P") - (transient--isearch-setup) - (let ((isearch-mode-map transient--isearch-mode-map)) - (isearch-mode nil regexp-p))) - -(defun transient-isearch-forward (&optional regexp-p) - "Do incremental search forward. -With a prefix argument, do an incremental regular expression -search instead." - (interactive "P") - (transient--isearch-setup) - (let ((isearch-mode-map transient--isearch-mode-map)) - (isearch-mode t regexp-p))) - -(defun transient-isearch-exit () - "Like `isearch-exit' but adapted for `transient'." - (interactive) - (isearch-exit) - (transient--isearch-exit)) - -(defun transient-isearch-cancel () - "Like `isearch-cancel' but adapted for `transient'." - (interactive) - (condition-case nil (isearch-cancel) (quit)) - (transient--isearch-exit)) - -(defun transient-isearch-abort () - "Like `isearch-abort' but adapted for `transient'." - (interactive) - (let ((around (lambda (fn) - (condition-case nil (funcall fn) (quit)) - (transient--isearch-exit)))) - (advice-add 'isearch-cancel :around around) - (unwind-protect - (isearch-abort) - (advice-remove 'isearch-cancel around)))) - -(defun transient--isearch-setup () - (select-window transient--window) - (transient--suspend-override t)) - -(defun transient--isearch-exit () - (select-window transient--original-window) - (transient--resume-override)) - -;;;; Edebug - -(defun transient--edebug-command-p () - (and (bound-and-true-p edebug-active) - (or (memq this-command '(top-level abort-recursive-edit)) - (string-prefix-p "edebug" (symbol-name this-command))))) - -;;;; Miscellaneous - -(cl-pushnew (list nil (concat "^\\s-*(" - (eval-when-compile - (regexp-opt - '("transient-define-prefix" - "transient-define-suffix" - "transient-define-infix" - "transient-define-argument") - t)) - "\\s-+\\(" lisp-mode-symbol-regexp "\\)") - 2) - lisp-imenu-generic-expression :test #'equal) - -(declare-function which-key-mode "which-key" (&optional arg)) - -(defun transient--suspend-which-key-mode () - (when (bound-and-true-p which-key-mode) - (which-key-mode -1) - (add-hook 'transient-exit-hook #'transient--resume-which-key-mode))) - -(defun transient--resume-which-key-mode () - (unless transient--prefix - (which-key-mode 1) - (remove-hook 'transient-exit-hook #'transient--resume-which-key-mode))) - -(defun transient-bind-q-to-quit () - "Modify some keymaps to bind \"q\" to the appropriate quit command. - -\"C-g\" is the default binding for such commands now, but Transient's -predecessor Magit-Popup used \"q\" instead. If you would like to get -that binding back, then call this function in your init file like so: - - (with-eval-after-load \\='transient - (transient-bind-q-to-quit)) - -Individual transients may already bind \"q\" to something else -and such a binding would shadow the quit binding. If that is the -case then \"Q\" is bound to whatever \"q\" would have been bound -to by setting `transient-substitute-key-function' to a function -that does that. Of course \"Q\" may already be bound to something -else, so that function binds \"M-q\" to that command instead. -Of course \"M-q\" may already be bound to something else, but -we stop there." - (keymap-set transient-base-map "q" #'transient-quit-one) - (keymap-set transient-sticky-map "q" #'transient-quit-seq) - (setq transient-substitute-key-function - #'transient-rebind-quit-commands)) - -(defun transient-rebind-quit-commands (obj) - "See `transient-bind-q-to-quit'." - (let ((key (oref obj key))) - (cond ((string-equal key "q") "Q") - ((string-equal key "Q") "M-q") - (key)))) - -(defun transient--force-fixed-pitch () - (require 'face-remap) - (face-remap-reset-base 'default) - (face-remap-add-relative 'default 'fixed-pitch)) - -(defun transient--func-arity (fn) - (func-arity (advice--cd*r (if (symbolp fn) (symbol-function fn) fn)))) - -(defun transient--seq-reductions-from (function sequence initial-value) - (let ((acc (list initial-value))) - (seq-doseq (elt sequence) - (push (funcall function (car acc) elt) acc)) - (nreverse acc))) - -;;; Font-Lock - -(defconst transient-font-lock-keywords - (eval-when-compile - `((,(concat "(" - (regexp-opt (list "transient-define-prefix" - "transient-define-infix" - "transient-define-argument" - "transient-define-suffix") - t) - "\\_>[ \t'(]*" - "\\(\\(?:\\sw\\|\\s_\\)+\\)?") - (1 'font-lock-keyword-face) - (2 'font-lock-function-name-face nil t))))) - -(font-lock-add-keywords 'emacs-lisp-mode transient-font-lock-keywords) - -;;; Auxiliary Classes -;;;; `transient-lisp-variable' - -(defclass transient-lisp-variable (transient-variable) - ((reader :initform #'transient-lisp-variable--reader) - (always-read :initform t) - (set-value :initarg :set-value :initform #'set)) - "[Experimental] Class used for Lisp variables.") - -(cl-defmethod transient-init-value ((obj transient-lisp-variable)) - (oset obj value (symbol-value (oref obj variable)))) - -(cl-defmethod transient-infix-set ((obj transient-lisp-variable) value) - (funcall (oref obj set-value) - (oref obj variable) - (oset obj value value))) - -(cl-defmethod transient-format-description ((obj transient-lisp-variable)) - (or (cl-call-next-method obj) - (symbol-name (oref obj variable)))) - -(cl-defmethod transient-format-value ((obj transient-lisp-variable)) - (propertize (prin1-to-string (oref obj value)) - 'face 'transient-value)) - -(cl-defmethod transient-prompt ((obj transient-lisp-variable)) - (if (and (slot-boundp obj 'prompt) - (oref obj prompt)) - (cl-call-next-method obj) - (format "Set %s: " (oref obj variable)))) - -(defun transient-lisp-variable--reader (prompt initial-input _history) - (read--expression prompt initial-input)) - -;;; _ -(provide 'transient) -;; Local Variables: -;; indent-tabs-mode: nil -;; checkdoc-symbol-words: ("command-line" "edit-mode" "help-mode") -;; End: -;;; transient.el ends here diff --git a/emacs/elpa/transient-20240525.1118/transient.elc b/emacs/elpa/transient-20240525.1118/transient.elc Binary files differ. diff --git a/emacs/elpa/transient-20240525.1118/transient.info b/emacs/elpa/transient-20240525.1118/transient.info @@ -1,3260 +0,0 @@ -This is transient.info, produced by makeinfo version 6.7 from -transient.texi. - - Copyright (C) 2018–2024 Free Software Foundation, Inc. - - You can redistribute this document and/or modify it under the terms - of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) - any later version. - - This document is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - -INFO-DIR-SECTION Emacs misc features -START-INFO-DIR-ENTRY -* Transient: (transient). Transient Commands. -END-INFO-DIR-ENTRY - - -File: transient.info, Node: Top, Next: Introduction, Up: (dir) - -Transient User and Developer Manual -*********************************** - -Transient is the library used to implement the keyboard-driven “menus” -in Magit. It is distributed as a separate package, so that it can be -used to implement similar menus in other packages. - - This manual can be bit hard to digest when getting started. A useful -resource to get over that hurdle is Psionic K’s interactive tutorial, -available at <https://github.com/positron-solutions/transient-showcase>. - -This manual is for Transient version 0.6.0. - - Copyright (C) 2018–2024 Free Software Foundation, Inc. - - You can redistribute this document and/or modify it under the terms - of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) - any later version. - - This document is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - -* Menu: - -* Introduction:: -* Usage:: -* Modifying Existing Transients:: -* Defining New Commands:: -* Classes and Methods:: -* FAQ:: -* Keystroke Index:: -* Command and Function Index:: -* Variable Index:: -* Concept Index:: -* GNU General Public License:: - -— The Detailed Node Listing — - -Usage - -* Invoking Transients:: -* Aborting and Resuming Transients:: -* Common Suffix Commands:: -* Saving Values:: -* Using History:: -* Getting Help for Suffix Commands:: -* Enabling and Disabling Suffixes:: -* Other Commands:: -* Configuration:: - -Defining New Commands - -* Technical Introduction:: -* Defining Transients:: -* Binding Suffix and Infix Commands:: -* Defining Suffix and Infix Commands:: -* Using Infix Arguments:: -* Transient State:: - -Binding Suffix and Infix Commands - -* Group Specifications:: -* Suffix Specifications:: - - -Classes and Methods - -* Group Classes:: -* Group Methods:: -* Prefix Classes:: -* Suffix Classes:: -* Suffix Methods:: -* Prefix Slots:: -* Suffix Slots:: -* Predicate Slots:: - -Suffix Methods - -* Suffix Value Methods:: -* Suffix Format Methods:: - - - - -File: transient.info, Node: Introduction, Next: Usage, Prev: Top, Up: Top - -1 Introduction -************** - -Transient is the library used to implement the keyboard-driven “menus” -in Magit. It is distributed as a separate package, so that it can be -used to implement similar menus in other packages. - - This manual can be bit hard to digest when getting started. A useful -resource to get over that hurdle is Psionic K’s interactive tutorial, -available at <https://github.com/positron-solutions/transient-showcase>. - -Some things that Transient can do -================================= - - • Display current state of arguments - • Display and manage lifecycle of modal bindings - • Contextual user interface - • Flow control for wizard-like composition of interactive forms - • History & persistence - • Rendering arguments for controlling CLI programs - -Complexity in CLI programs -========================== - -Complexity tends to grow with time. How do you manage the complexity of -commands? Consider the humble shell command ‘ls’. It now has over -_fifty_ command line options. Some of these are boolean flags (‘ls --l’). Some take arguments (‘ls --sort=s’). Some have no effect unless -paired with other flags (‘ls -lh’). Some are mutually exclusive. Some -shell commands even have so many options that they introduce -_subcommands_ (‘git branch’, ‘git commit’), each with their own rich set -of options (‘git branch -f’). - -Using Transient for composing interactive commands -================================================== - -What about Emacs commands used interactively? How do these handle -options? One solution is to make many versions of the same command, so -you don’t need to! Consider: ‘delete-other-windows’ vs. -‘delete-other-windows-vertically’ (among many similar examples). - - Some Emacs commands will simply prompt you for the next "argument" -(‘M-x switch-to-buffer’). Another common solution is to use prefix -arguments which usually start with ‘C-u’. Sometimes these are sensibly -numerical in nature (‘C-u 4 M-x forward-paragraph’ to move forward 4 -paragraphs). But sometimes they function instead as boolean "switches" -(‘C-u C-SPACE’ to jump to the last mark instead of just setting it, ‘C-u -C-u C-SPACE’ to unconditionally set the mark). Since there aren’t many -standards for the use of prefix options, you have to read the command’s -documentation to find out what the possibilities are. - - But when an Emacs command grows to have a truly large set of options -and arguments, with dependencies between them, lots of option values, -etc., these simple approaches just don’t scale. Transient is designed -to solve this issue. Think of it as the humble prefix argument ‘C-u’, -_raised to the power of 10_. Like ‘C-u’, it is key driven. Like the -shell, it supports boolean "flag" options, options that take arguments, -and even "sub-commands", with their own options. But instead of -searching through a man page or command documentation, well-designed -transients _guide_ their users to the relevant set of options (and even -their possible values!) directly, taking into account any important -pre-existing Emacs settings. And while for shell commands like ‘ls’, -there is only one way to "execute" (hit ‘Return’!), transients can -"execute" using multiple different keys tied to one of many -self-documenting _actions_ (imagine having 5 different colored return -keys on your keyboard!). Transients make navigating and setting large, -complex groups of command options and arguments easy. Fun even. Once -you’ve tried it, it’s hard to go back to the ‘C-u what can I do here -again?’ way. - - -File: transient.info, Node: Usage, Next: Modifying Existing Transients, Prev: Introduction, Up: Top - -2 Usage -******* - -* Menu: - -* Invoking Transients:: -* Aborting and Resuming Transients:: -* Common Suffix Commands:: -* Saving Values:: -* Using History:: -* Getting Help for Suffix Commands:: -* Enabling and Disabling Suffixes:: -* Other Commands:: -* Configuration:: - - -File: transient.info, Node: Invoking Transients, Next: Aborting and Resuming Transients, Up: Usage - -2.1 Invoking Transients -======================= - -A transient prefix command is invoked like any other command by pressing -the key that is bound to that command. The main difference to other -commands is that a transient prefix command activates a transient -keymap, which temporarily binds the transient’s infix and suffix -commands. Bindings from other keymaps may, or may not, be disabled -while the transient state is in effect. - - There are two kinds of commands that are available after invoking a -transient prefix command; infix and suffix commands. Infix commands set -some value (which is then shown in a popup buffer), without leaving the -transient. Suffix commands, on the other hand, usually quit the -transient and they may use the values set by the infix commands, i.e., -the infix *arguments*. - - Instead of setting arguments to be used by a suffix command, infix -commands may also set some value by side-effect, e.g., by setting the -value of some variable. - - -File: transient.info, Node: Aborting and Resuming Transients, Next: Common Suffix Commands, Prev: Invoking Transients, Up: Usage - -2.2 Aborting and Resuming Transients -==================================== - -To quit the transient without invoking a suffix command press ‘C-g’. - - Key bindings in transient keymaps may be longer than a single event. -After pressing a valid prefix key, all commands whose bindings do not -begin with that prefix key are temporarily unavailable and grayed out. -To abort the prefix key press ‘C-g’ (which in this case only quits the -prefix key, but not the complete transient). - - A transient prefix command can be bound as a suffix of another -transient. Invoking such a suffix replaces the current transient state -with a new transient state, i.e., the available bindings change and the -information displayed in the popup buffer is updated accordingly. -Pressing ‘C-g’ while a nested transient is active only quits the -innermost transient, causing a return to the previous transient. - - ‘C-q’ or ‘C-z’ on the other hand always exits all transients. If you -use the latter, then you can later resume the stack of transients using -‘M-x transient-resume’. - -‘C-g’ (‘transient-quit-seq’) -‘C-g’ (‘transient-quit-one’) - This key quits the currently active incomplete key sequence, if - any, or else the current transient. When quitting the current - transient, it returns to the previous transient, if any. - - Transient’s predecessor bound ‘q’ instead of ‘C-g’ to the quit -command. To learn how to get that binding back see -‘transient-bind-q-to-quit’’s documentation string. - -‘C-q’ (‘transient-quit-all’) - This command quits the currently active incomplete key sequence, if - any, and all transients, including the active transient and all - suspended transients, if any. - -‘C-z’ (‘transient-suspend’) - Like ‘transient-quit-all’, this command quits an incomplete key - sequence, if any, and all transients. Additionally, it saves the - stack of transients so that it can easily be resumed (which is - particularly useful if you quickly need to do “something else” and - the stack is deeper than a single transient, and/or you have - already changed the values of some infix arguments). - - Note that only a single stack of transients can be saved at a time. - If another stack is already saved, then saving a new stack discards - the previous stack. - -‘M-x transient-resume’ - This command resumes the previously suspended stack of transients, - if any. - - -File: transient.info, Node: Common Suffix Commands, Next: Saving Values, Prev: Aborting and Resuming Transients, Up: Usage - -2.3 Common Suffix Commands -========================== - -A few shared suffix commands are available in all transients. These -suffix commands are not shown in the popup buffer by default. - - This includes the aborting commands mentioned in the previous -section, as well as some other commands that are all bound to ‘C-x KEY’. -After ‘C-x’ is pressed, a section featuring all these common commands is -temporarily shown in the popup buffer. After invoking one of them, the -section disappears again. Note, however, that one of these commands is -described as “Show common permanently”; invoke that if you want the -common commands to always be shown for all transients. - -‘C-x t’ (‘transient-toggle-common’) - This command toggles whether the generic commands that are common - to all transients are always displayed or only after typing the - incomplete prefix key sequence ‘C-x’. This only affects the - current Emacs session. - - -- User Option: transient-show-common-commands - This option controls whether shared suffix commands are shown - alongside the transient-specific infix and suffix commands. By - default, the shared commands are not shown to avoid overwhelming - the user with too many options. - - While a transient is active, pressing ‘C-x’ always shows the common - commands. The value of this option can be changed for the current - Emacs session by typing ‘C-x t’ while a transient is active. - - The other common commands are described in either the previous or in -one of the following sections. - - Some of Transient’s key bindings differ from the respective bindings -of Magit-Popup; see *note FAQ:: for more information. - - -File: transient.info, Node: Saving Values, Next: Using History, Prev: Common Suffix Commands, Up: Usage - -2.4 Saving Values -================= - -After setting the infix arguments in a transient, the user can save -those arguments for future invocations. - - Most transients will start out with the saved arguments when they are -invoked. There are a few exceptions, though. Some transients are -designed so that the value that they use is stored externally as the -buffer-local value of some variable. Invoking such a transient again -uses the buffer-local value. (1) - - If the user does not save the value and just exits using a regular -suffix command, then the value is merely saved to the transient’s -history. That value won’t be used when the transient is next invoked, -but it is easily accessible (see *note Using History::). - -‘C-x s’ (‘transient-set’) - This command saves the value of the active transient for this Emacs - session. - -‘C-x C-s’ (‘transient-save’) - Save the value of the active transient persistently across Emacs - sessions. - -‘C-x C-k’ (‘transient-reset’) - Clear the set and saved values of the active transient. - - -- User Option: transient-values-file - This option names the file that is used to persist the values of - transients between Emacs sessions. - - ---------- Footnotes ---------- - - (1) ‘magit-diff’ and ‘magit-log’ are two prominent examples, and -their handling of buffer-local values is actually a bit more complicated -than outlined above and even customizable. - - -File: transient.info, Node: Using History, Next: Getting Help for Suffix Commands, Prev: Saving Values, Up: Usage - -2.5 Using History -================= - -Every time the user invokes a suffix command the transient’s current -value is saved to its history. These values can be cycled through the -same way one can cycle through the history of commands that read -user-input in the minibuffer. - -‘C-M-p’ (‘transient-history-prev’) -‘C-x p’ - This command switches to the previous value used for the active - transient. - -‘C-M-n’ (‘transient-history-next’) -‘C-x n’ - This command switches to the next value used for the active - transient. - - In addition to the transient-wide history, Transient of course -supports per-infix history. When an infix reads user-input using the -minibuffer, the user can use the regular minibuffer history commands to -cycle through previously used values. Usually the same keys as those -mentioned above are bound to those commands. - - Authors of transients should arrange for different infix commands -that read the same kind of value to also use the same history key (see -*note Suffix Slots::). - - Both kinds of history are saved to a file when Emacs is exited. - - -- User Option: transient-history-file - This option names the file that is used to persist the history of - transients and their infixes between Emacs sessions. - - -- User Option: transient-history-limit - This option controls how many history elements are kept at the time - the history is saved in ‘transient-history-file’. - - -File: transient.info, Node: Getting Help for Suffix Commands, Next: Enabling and Disabling Suffixes, Prev: Using History, Up: Usage - -2.6 Getting Help for Suffix Commands -==================================== - -Transients can have many suffixes and infixes that the user might not be -familiar with. To make it trivial to get help for these, Transient -provides access to the documentation directly from the active transient. - -‘C-h’ (‘transient-help’) - This command enters help mode. When help mode is active, typing a - key shows information about the suffix command that the key - normally is bound to (instead of invoking it). Pressing ‘C-h’ a - second time shows information about the _prefix_ command. - - After typing a key, the stack of transient states is suspended and - information about the suffix command is shown instead. Typing ‘q’ - in the help buffer buries that buffer and resumes the transient - state. - - What sort of documentation is shown depends on how the transient was -defined. For infix commands that represent command-line arguments this -ideally shows the appropriate manpage. ‘transient-help’ then tries to -jump to the correct location within that. Info manuals are also -supported. The fallback is to show the command’s documentation string, -for non-infix suffixes this is usually appropriate. - - -File: transient.info, Node: Enabling and Disabling Suffixes, Next: Other Commands, Prev: Getting Help for Suffix Commands, Up: Usage - -2.7 Enabling and Disabling Suffixes -=================================== - -The user base of a package that uses transients can be very diverse. -This is certainly the case for Magit; some users have been using it and -Git for a decade, while others are just getting started now. - - For that reason a mechanism is needed that authors can use to -classify a transient’s infixes and suffixes along the -essentials...everything spectrum. We use the term “levels” to describe -that mechanism. - - Each suffix command is placed on a level and each transient has a -level (called “transient-level”), which controls which suffix commands -are available. Integers between 1 and 7 (inclusive) are valid levels. -For suffixes, 0 is also valid; it means that the suffix is not displayed -at any level. - - The levels of individual transients and/or their individual suffixes -can be changed interactively, by invoking the transient and then -pressing ‘C-x l’ to enter the “edit” mode, see below. - - The default level for both transients and their suffixes is 4. The -‘transient-default-level’ option only controls the default for -transients. The default suffix level is always 4. The authors of -transients should place certain suffixes on a higher level, if they -expect that it won’t be of use to most users, and they should place very -important suffixes on a lower level, so that they remain available even -if the user lowers the transient level. - - -- User Option: transient-default-level - This option controls which suffix levels are made available by - default. It sets the transient-level for transients for which the - user has not set that individually. - - -- User Option: transient-levels-file - This option names the file that is used to persist the levels of - transients and their suffixes between Emacs sessions. - -‘C-x l’ (‘transient-set-level’) - This command enters edit mode. When edit mode is active, then all - infixes and suffixes that are currently usable are displayed along - with their levels. The colors of the levels indicate whether they - are enabled or not. The level of the transient is also displayed - along with some usage information. - - In edit mode, pressing the key that would usually invoke a certain - suffix instead prompts the user for the level that suffix should be - placed on. - - Help mode is available in edit mode. - - To change the transient level press ‘C-x l’ again. - - To exit edit mode press ‘C-g’. - - Note that edit mode does not display any suffixes that are not - currently usable. ‘magit-rebase’, for example, shows different - suffixes depending on whether a rebase is already in progress or - not. The predicates also apply in edit mode. - - Therefore, to control which suffixes are available given a certain - state, you have to make sure that that state is currently active. - -‘C-x a’ (‘transient-toggle-level-limit’) - This command toggle whether suffixes that are on levels higher than - the level specified by ‘transient-default-level’ are temporarily - available anyway. - - -File: transient.info, Node: Other Commands, Next: Configuration, Prev: Enabling and Disabling Suffixes, Up: Usage - -2.8 Other Commands -================== - -When invoking a transient in a small frame, the transient window may not -show the complete buffer, making it necessary to scroll, using the -following commands. These commands are never shown in the transient -window, and the key bindings are the same as for ‘scroll-up-command’ and -‘scroll-down-command’ in other buffers. - - -- Command: transient-scroll-up arg - This command scrolls text of transient popup window upward ARG - lines. If ARG is ‘nil’, then it scrolls near full screen. This is - a wrapper around ‘scroll-up-command’ (which see). - - -- Command: transient-scroll-down arg - This command scrolls text of transient popup window down ARG lines. - If ARG is ‘nil’, then it scrolls near full screen. This is a - wrapper around ‘scroll-down-command’ (which see). - - -File: transient.info, Node: Configuration, Prev: Other Commands, Up: Usage - -2.9 Configuration -================= - -More options are described in *note Common Suffix Commands::, in *note -Saving Values::, in *note Using History:: and in *note Enabling and -Disabling Suffixes::. - -Essential Options ------------------ - -Also see *note Common Suffix Commands::. - - -- User Option: transient-show-popup - This option controls whether the current transient’s infix and - suffix commands are shown in the popup buffer. - - • If ‘t’ (the default) then the popup buffer is shown as soon as - a transient prefix command is invoked. - - • If ‘nil’, then the popup buffer is not shown unless the user - explicitly requests it, by pressing an incomplete prefix key - sequence. - - • If a number, then the a brief one-line summary is shown - instead of the popup buffer. If zero or negative, then not - even that summary is shown; only the pressed key itself is - shown. - - The popup is shown when the user explicitly requests it by - pressing an incomplete prefix key sequence. Unless this is - zero, the popup is shown after that many seconds of inactivity - (using the absolute value). - - -- User Option: transient-enable-popup-navigation - This option controls whether navigation commands are enabled in the - transient popup buffer. - - While a transient is active the transient popup buffer is not the - current buffer, making it necessary to use dedicated commands to - act on that buffer itself. This is disabled by default. If this - option is non-‘nil’, then the following features are available: - - • ‘<UP>’ moves the cursor to the previous suffix. - • ‘<DOWN>’ moves the cursor to the next suffix. - • ‘<RET>’ invokes the suffix the cursor is on. - • ‘mouse-1’ invokes the clicked on suffix. - • ‘C-s’ and ‘C-r’ start isearch in the popup buffer. - - -- User Option: transient-display-buffer-action - This option specifies the action used to display the transient - popup buffer. The transient popup buffer is displayed in a window - using ‘(display-buffer BUFFER transient-display-buffer-action)’. - - The value of this option has the form ‘(FUNCTION . ALIST)’, where - FUNCTION is a function or a list of functions. Each such function - should accept two arguments: a buffer to display and an alist of - the same form as ALIST. See *note (elisp)Choosing Window::, for - details. - - The default is: - - (display-buffer-in-side-window - (side . bottom) - (inhibit-same-window . t) - (window-parameters (no-other-window . t))) - - This displays the window at the bottom of the selected frame. - Another useful FUNCTION is ‘display-buffer-below-selected’, which - is what ‘magit-popup’ used by default. For more alternatives see - *note (elisp)Buffer Display Action Functions::, and *note - (elisp)Buffer Display Action Alists::. - - Note that the buffer that was current before the transient buffer - is shown should remain the current buffer. Many suffix commands - act on the thing at point, if appropriate, and if the transient - buffer became the current buffer, then that would change what is at - point. To that effect ‘inhibit-same-window’ ensures that the - selected window is not used to show the transient buffer. - - It may be possible to display the window in another frame, but - whether that works in practice depends on the window-manager. If - the window manager selects the new window (Emacs frame), then that - unfortunately changes which buffer is current. - - If you change the value of this option, then you might also want to - change the value of ‘transient-mode-line-format’. - -Accessibility Options ---------------------- - - -- User Option: transient-force-single-column - This option controls whether the use of a single column to display - suffixes is enforced. This might be useful for users with low - vision who use large text and might otherwise have to scroll in two - dimensions. - -Auxiliary Options ------------------ - - -- User Option: transient-mode-line-format - This option controls whether the transient popup buffer has a - mode-line, separator line, or neither. - - If ‘nil’, then the buffer has no mode-line. If the buffer is not - displayed right above the echo area, then this probably is not a - good value. - - If ‘line’ (the default) or a natural number, then the buffer has no - mode-line, but a line is drawn is drawn in its place. If a number - is used, that specifies the thickness of the line. On termcap - frames we cannot draw lines, so there ‘line’ and numbers are - synonyms for ‘nil’. - - The color of the line is used to indicate if non-suffixes are - allowed and whether they exit the transient. The foreground color - of ‘transient-key-noop’ (if non-suffix are disallowed), - ‘transient-key-stay’ (if allowed and transient stays active), or - ‘transient-key-exit’ (if allowed and they exit the transient) is - used to draw the line. - - Otherwise this can be any mode-line format. See *note (elisp)Mode - Line Format::, for details. - - -- User Option: transient-semantic-coloring - This option controls whether colors are used to indicate the - transient behavior of commands. - - If non-‘nil’, then the key binding of each suffix is colorized to - indicate whether it exits the transient state or not. The color of - the prefix is indicated using the line that is drawn when the value - of ‘transient-mode-line-format’ is ‘line’. - - -- User Option: transient-highlight-mismatched-keys - This option controls whether key bindings of infix commands that do - not match the respective command-line argument should be - highlighted. For other infix commands this option has no effect. - - When this option is non-‘nil’, the key binding for an infix - argument is highlighted when only a long argument (e.g., - ‘--verbose’) is specified but no shorthand (e.g., ‘-v’). In the - rare case that a shorthand is specified but the key binding does - not match, then it is highlighted differently. - - Highlighting mismatched key bindings is useful when learning the - arguments of the underlying command-line tool; you wouldn’t want to - learn any short-hands that do not actually exist. - - The highlighting is done using one of the faces - ‘transient-mismatched-key’ and ‘transient-nonstandard-key’. - - -- User Option: transient-substitute-key-function - This function is used to modify key bindings. If the value of this - option is ‘nil’ (the default), then no substitution is performed. - - This function is called with one argument, the prefix object, and - must return a key binding description, either the existing key - description it finds in the ‘key’ slot, or the key description that - replaces the prefix key. It could be used to make other - substitutions, but that is discouraged. - - For example, ‘=’ is hard to reach using my custom keyboard layout, - so I substitute ‘(’ for that, which is easy to reach using a layout - optimized for lisp. - - (setq transient-substitute-key-function - (lambda (obj) - (let ((key (oref obj key))) - (if (string-match "\\`\\(=\\)[a-zA-Z]" key) - (replace-match "(" t t key 1) - key)))) - - -- User Option: transient-read-with-initial-input - This option controls whether the last history element is used as - the initial minibuffer input when reading the value of an infix - argument from the user. If ‘nil’, there is no initial input and - the first element has to be accessed the same way as the older - elements. - - -- User Option: transient-hide-during-minibuffer-read - This option controls whether the transient buffer is hidden while - user input is being read in the minibuffer. - - -- User Option: transient-align-variable-pitch - This option controls whether columns are aligned pixel-wise in the - popup buffer. - - If this is non-‘nil’, then columns are aligned pixel-wise to - support variable-pitch fonts. Keys are not aligned, so you should - use a fixed-pitch font for the ‘transient-key’ face. Other key - faces inherit from that face unless a theme is used that breaks - that relationship. - - This option is intended for users who use a variable-pitch font for - the ‘default’ face. - - -- User Option: transient-force-fixed-pitch - This option controls whether to force the use of a monospaced font - in popup buffer. Even if you use a proportional font for the - ‘default’ face, you might still want to use a monospaced font in - transient’s popup buffer. Setting this option to ‘t’ causes - ‘default’ to be remapped to ‘fixed-pitch’ in that buffer. - -Developer Options ------------------ - -These options are mainly intended for developers. - - -- User Option: transient-detect-key-conflicts - This option controls whether key binding conflicts should be - detected at the time the transient is invoked. If so, this results - in an error, which prevents the transient from being used. Because - of that, conflicts are ignored by default. - - Conflicts cannot be determined earlier, i.e., when the transient is - being defined and when new suffixes are being added, because at - that time there can be false-positives. It is actually valid for - multiple suffixes to share a common key binding, provided the - predicates of those suffixes prevent that more than one of them is - enabled at a time. - - -- User Option: transient-highlight-higher-levels - This option controls whether suffixes that would not be available - by default are highlighted. - - When non-‘nil’ then the descriptions of suffixes are highlighted if - their level is above 4, the default of ‘transient-default-level’. - Assuming you have set that variable to 7, this highlights all - suffixes that won’t be available to users without them making the - same customization. - - -File: transient.info, Node: Modifying Existing Transients, Next: Defining New Commands, Prev: Usage, Up: Top - -3 Modifying Existing Transients -******************************* - -To an extent, transients can be customized interactively, see *note -Enabling and Disabling Suffixes::. This section explains how existing -transients can be further modified non-interactively. Let’s begin with -an example: - - (transient-append-suffix 'magit-patch-apply "-3" - '("-R" "Apply in reverse" "--reverse")) - - This inserts a new infix argument to toggle the ‘--reverse’ argument -after the infix argument that toggles ‘-3’ in ‘magit-patch-apply’. - - The following functions share a few arguments: - - • PREFIX is a transient prefix command, a symbol. - - • SUFFIX is a transient infix or suffix specification in the same - form as expected by ‘transient-define-prefix’. Note that an infix - is a special kind of suffix. Depending on context “suffixes” means - “suffixes (including infixes)” or “non-infix suffixes”. Here it - means the former. See *note Suffix Specifications::. - - SUFFIX may also be a group in the same form as expected by - ‘transient-define-prefix’. See *note Group Specifications::. - - • LOC is a command, a key vector, a key description (a string as - returned by ‘key-description’), or a list specifying coordinates - (the last element may also be a command or key). For example ‘(1 0 - -1)’ identifies the last suffix (‘-1’) of the first subgroup (‘0’) - of the second group (‘1’). - - If LOC is a list of coordinates, then it can be used to identify a - group, not just an individual suffix command. - - The function ‘transient-get-suffix’ can be useful to determine - whether a certain coordination list identifies the suffix or group - that you expect it to identify. In hairy cases it may be necessary - to look at the definition of the transient prefix command. - - These functions operate on the information stored in the -‘transient--layout’ property of the PREFIX symbol. Suffix entries in -that tree are not objects but have the form ‘(LEVEL CLASS PLIST)’, where -PLIST should set at least ‘:key’, ‘:description’ and ‘:command’. - - -- Function: transient-insert-suffix prefix loc suffix &optional - keep-other - -- Function: transient-append-suffix prefix loc suffix &optional - keep-other - These functions insert the suffix or group SUFFIX into PREFIX - before or after LOC. - - Conceptually adding a binding to a transient prefix is similar to - adding a binding to a keymap, but this is complicated by the fact - that multiple suffix commands can be bound to the same key, - provided they are never active at the same time, see *note - Predicate Slots::. - - Unfortunately both false-positives and false-negatives are - possible. To deal with the former use non-‘nil’ KEEP-OTHER. To - deal with the latter remove the conflicting binding explicitly. - - -- Function: transient-replace-suffix prefix loc suffix - This function replaces the suffix or group at LOC in PREFIX with - suffix or group SUFFIX. - - -- Function: transient-remove-suffix prefix loc - This function removes the suffix or group at LOC in PREFIX. - - -- Function: transient-get-suffix prefix loc - This function returns the suffix or group at LOC in PREFIX. The - returned value has the form mentioned above. - - -- Function: transient-suffix-put prefix loc prop value - This function edits the suffix or group at LOC in PREFIX, by - setting the PROP of its plist to VALUE. - - Most of these functions do not signal an error if they cannot perform -the requested modification. The functions that insert new suffixes show -a warning if LOC cannot be found in PREFIX without signaling an error. -The reason for doing it like this is that establishing a key binding -(and that is what we essentially are trying to do here) should not -prevent the rest of the configuration from loading. Among these -functions only ‘transient-get-suffix’ and ‘transient-suffix-put’ may -signal an error. - - -File: transient.info, Node: Defining New Commands, Next: Classes and Methods, Prev: Modifying Existing Transients, Up: Top - -4 Defining New Commands -*********************** - -* Menu: - -* Technical Introduction:: -* Defining Transients:: -* Binding Suffix and Infix Commands:: -* Defining Suffix and Infix Commands:: -* Using Infix Arguments:: -* Transient State:: - - -File: transient.info, Node: Technical Introduction, Next: Defining Transients, Up: Defining New Commands - -4.1 Technical Introduction -========================== - -Taking inspiration from prefix keys and prefix arguments, Transient -implements a similar abstraction involving a prefix command, infix -arguments and suffix commands. - - When the user calls a transient prefix command, a transient -(temporary) keymap is activated, which binds the transient’s infix and -suffix commands, and functions that control the transient state are -added to ‘pre-command-hook’ and ‘post-command-hook’. The available -suffix and infix commands and their state are shown in a popup buffer -until the transient state is exited by invoking a suffix command. - - Calling an infix command causes its value to be changed. How that is -done depends on the type of the infix command. The simplest case is an -infix command that represents a command-line argument that does not take -a value. Invoking such an infix command causes the switch to be toggled -on or off. More complex infix commands may read a value from the user, -using the minibuffer. - - Calling a suffix command usually causes the transient to be exited; -the transient keymaps and hook functions are removed, the popup buffer -no longer shows information about the (no longer bound) suffix commands, -the values of some public global variables are set, while some internal -global variables are unset, and finally the command is actually called. -Suffix commands can also be configured to not exit the transient. - - A suffix command can, but does not have to, use the infix arguments -in much the same way any command can choose to use or ignore the prefix -arguments. For a suffix command that was invoked from a transient, the -variable ‘transient-current-suffixes’ and the function ‘transient-args’ -serve about the same purpose as the variables ‘prefix-arg’ and -‘current-prefix-arg’ do for any command that was called after the prefix -arguments have been set using a command such as ‘universal-argument’. - - Transient can be used to implement simple “command dispatchers”. The -main benefit then is that the user can see all the available commands in -a popup buffer, which can be thought of as a “menus”. That is useful by -itself because it frees the user from having to remember all the keys -that are valid after a certain prefix key or command. Magit’s -‘magit-dispatch’ (on ‘C-x M-g’) command is an example of using Transient -to merely implement a command dispatcher. - - In addition to that, Transient also allows users to interactively -pass arguments to commands. These arguments can be much more complex -than what is reasonable when using prefix arguments. There is a limit -to how many aspects of a command can be controlled using prefix -arguments. Furthermore, what a certain prefix argument means for -different commands can be completely different, and users have to read -documentation to learn and then commit to memory what a certain prefix -argument means to a certain command. - - Transient suffix commands, on the other hand, can accept dozens of -different arguments without the user having to remember anything. When -using Transient, one can call a command with arguments that are just as -complex as when calling the same function non-interactively from Lisp. - - Invoking a transient suffix command with arguments is similar to -invoking a command in a shell with command-line completion and history -enabled. One benefit of the Transient interface is that it remembers -history not only on a global level (“this command was invoked using -these arguments, and previously it was invoked using those other -arguments”), but also remembers the values of individual arguments -independently. See *note Using History::. - - After a transient prefix command is invoked, ‘C-h KEY’ can be used to -show the documentation for the infix or suffix command that ‘KEY’ is -bound to (see *note Getting Help for Suffix Commands::), and infixes and -suffixes can be removed from the transient using ‘C-x l KEY’. Infixes -and suffixes that are disabled by default can be enabled the same way. -See *note Enabling and Disabling Suffixes::. - - Transient ships with support for a few different types of specialized -infix commands. A command that sets a command line option, for example, -has different needs than a command that merely toggles a boolean flag. -Additionally, Transient provides abstractions for defining new types, -which the author of Transient did not anticipate (or didn’t get around -to implementing yet). - - Note that suffix commands also support regular prefix arguments. A -suffix command may even be called with both infix and prefix arguments -at the same time. If you invoke a command as a suffix of a transient -prefix command, but also want to pass prefix arguments to it, then first -invoke the prefix command, and only after doing that invoke the prefix -arguments, before finally invoking the suffix command. If you instead -began by providing the prefix arguments, then those would apply to the -prefix command, not the suffix command. Likewise, if you want to change -infix arguments before invoking a suffix command with prefix arguments, -then change the infix arguments before invoking the prefix arguments. -In other words, regular prefix arguments always apply to the next -command, and since transient prefix, infix and suffix commands are just -regular commands, the same applies to them. (Regular prefix keys behave -differently because they are not commands at all, instead they are just -incomplete key sequences, and those cannot be interrupted with prefix -commands.) - - -File: transient.info, Node: Defining Transients, Next: Binding Suffix and Infix Commands, Prev: Technical Introduction, Up: Defining New Commands - -4.2 Defining Transients -======================= - -A transient consists of a prefix command and at least one suffix -command, though usually a transient has several infix and suffix -commands. The below macro defines the transient prefix command *and* -binds the transient’s infix and suffix commands. In other words, it -defines the complete transient, not just the transient prefix command -that is used to invoke that transient. - - -- Macro: transient-define-prefix name arglist [docstring] [keyword - value]... group... [body...] - This macro defines NAME as a transient prefix command and binds the - transient’s infix and suffix commands. - - ARGLIST are the arguments that the prefix command takes. DOCSTRING - is the documentation string and is optional. - - These arguments can optionally be followed by keyword-value pairs. - Each key has to be a keyword symbol, either ‘:class’ or a keyword - argument supported by the constructor of that class. The - ‘transient-prefix’ class is used if the class is not specified - explicitly. - - GROUPs add key bindings for infix and suffix commands and specify - how these bindings are presented in the popup buffer. At least one - GROUP has to be specified. See *note Binding Suffix and Infix - Commands::. - - The BODY is optional. If it is omitted, then ARGLIST is ignored - and the function definition becomes: - - (lambda () - (interactive) - (transient-setup 'NAME)) - - If BODY is specified, then it must begin with an ‘interactive’ form - that matches ARGLIST, and it must call ‘transient-setup’. It may, - however, call that function only when some condition is satisfied. - - All transients have a (possibly ‘nil’) value, which is exported - when suffix commands are called, so that they can consume that - value. For some transients it might be necessary to have a sort of - secondary value, called a “scope”. Such a scope would usually be - set in the command’s ‘interactive’ form and has to be passed to the - setup function: - - (transient-setup 'NAME nil nil :scope SCOPE) - - For example, the scope of the ‘magit-branch-configure’ transient is - the branch whose variables are being configured. - - -File: transient.info, Node: Binding Suffix and Infix Commands, Next: Defining Suffix and Infix Commands, Prev: Defining Transients, Up: Defining New Commands - -4.3 Binding Suffix and Infix Commands -===================================== - -The macro ‘transient-define-prefix’ is used to define a transient. This -defines the actual transient prefix command (see *note Defining -Transients::) and adds the transient’s infix and suffix bindings, as -described below. - - Users and third-party packages can add additional bindings using -functions such as ‘transient-insert-suffix’ (see *note Modifying -Existing Transients::). These functions take a “suffix specification” -as one of their arguments, which has the same form as the specifications -used in ‘transient-define-prefix’. - -* Menu: - -* Group Specifications:: -* Suffix Specifications:: - - -File: transient.info, Node: Group Specifications, Next: Suffix Specifications, Up: Binding Suffix and Infix Commands - -4.3.1 Group Specifications --------------------------- - -The suffix and infix commands of a transient are organized in groups. -The grouping controls how the descriptions of the suffixes are outlined -visually but also makes it possible to set certain properties for a set -of suffixes. - - Several group classes exist, some of which organize suffixes in -subgroups. In most cases the class does not have to be specified -explicitly, but see *note Group Classes::. - - Groups are specified in the call to ‘transient-define-prefix’, using -vectors. Because groups are represented using vectors, we cannot use -square brackets to indicate an optional element and instead use curly -brackets to do the latter. - - Group specifications then have this form: - - [{LEVEL} {DESCRIPTION} {KEYWORD VALUE}... ELEMENT...] - - The LEVEL is optional and defaults to 4. See *note Enabling and -Disabling Suffixes::. - - The DESCRIPTION is optional. If present, it is used as the heading -of the group. - - The KEYWORD-VALUE pairs are optional. Each keyword has to be a -keyword symbol, either ‘:class’ or a keyword argument supported by the -constructor of that class. - - • One of these keywords, ‘:description’, is equivalent to specifying - DESCRIPTION at the very beginning of the vector. The - recommendation is to use ‘:description’ if some other keyword is - also used, for consistency, or DESCRIPTION otherwise, because it - looks better. - - • Likewise ‘:level’ is equivalent to LEVEL. - - • Other important keywords include the ‘:if...’ keywords. These - keywords control whether the group is available in a certain - situation. - - For example, one group of the ‘magit-rebase’ transient uses ‘:if - magit-rebase-in-progress-p’, which contains the suffixes that are - useful while rebase is already in progress; and another that uses - ‘:if-not magit-rebase-in-progress-p’, which contains the suffixes - that initiate a rebase. - - These predicates can also be used on individual suffixes and are - only documented once, see *note Predicate Slots::. - - • The value of ‘:hide’, if non-‘nil’, is a predicate that controls - whether the group is hidden by default. The key bindings for - suffixes of a hidden group should all use the same prefix key. - Pressing that prefix key should temporarily show the group and its - suffixes, which assumes that a predicate like this is used: - - (lambda () - (eq (car transient--redisplay-key) - ?\C-c)) ; the prefix key shared by all bindings - - • The value of ‘:setup-children’, if non-‘nil’, is a function that - takes one argument, a potentially list of children, and must return - a list of children or an empty list. This can either be used to - somehow transform the group’s children that were defined the normal - way, or to dynamically create the children from scratch. - - The returned children must have the same form as stored in the - prefix’s ‘transient--layout’ property, but it is often more - convenient to use the same form as understood by - ‘transient-define-prefix’, described below. If you use the latter - approach, you can use the ‘transient-parse-suffixes’ and - ‘transient-parse-suffix’ functions to transform them from the - convenient to the expected form. - - If you explicitly specify children and then transform them using - ‘:setup-chilren’, then the class of the group is determined as - usual, based on explicitly specified children. - - If you do not explicitly specify children and thus rely solely on - ‘:setup-children’, then you must specify the class using ‘:class’. - For backward compatibility, if you fail to do so, - ‘transient-column’ is used and a warning is displayed. This - warning will eventually be replaced with an error. - - • The boolean ‘:pad-keys’ argument controls whether keys of all - suffixes contained in a group are right padded, effectively - aligning the descriptions. - - The ELEMENTs are either all subgroups, or all suffixes and strings. -(At least currently no group type exists that would allow mixing -subgroups with commands at the same level, though in principle there is -nothing that prevents that.) - - If the ELEMENTs are not subgroups, then they can be a mixture of -lists, which specify commands, and strings. Strings are inserted -verbatim into the buffer. The empty string can be used to insert gaps -between suffixes, which is particularly useful if the suffixes are -outlined as a table. - - Inside group specifications, including inside contained suffix -specifications, nothing has to be quoted and quoting anyway is invalid. -The value following a keyword, can be explicitly unquoted using ‘,’. -This feature is experimental and should be avoided. - - The form of suffix specifications is documented in the next node. - - -File: transient.info, Node: Suffix Specifications, Prev: Group Specifications, Up: Binding Suffix and Infix Commands - -4.3.2 Suffix Specifications ---------------------------- - -A transient’s suffix and infix commands are bound when the transient -prefix command is defined using ‘transient-define-prefix’, see *note -Defining Transients::. The commands are organized into groups, see -*note Group Specifications::. Here we describe the form used to bind an -individual suffix command. - - The same form is also used when later binding additional commands -using functions such as ‘transient-insert-suffix’, see *note Modifying -Existing Transients::. - - Note that an infix is a special kind of suffix. Depending on context -“suffixes” means “suffixes (including infixes)” or “non-infix suffixes”. -Here it means the former. - - Suffix specifications have this form: - - ([LEVEL] [KEY [DESCRIPTION]] COMMAND|ARGUMENT [KEYWORD VALUE]...) - - LEVEL, KEY and DESCRIPTION can also be specified using the KEYWORDs -‘:level’, ‘:key’ and ‘:description’. If the object that is associated -with COMMAND sets these properties, then they do not have to be -specified here. You can however specify them here anyway, possibly -overriding the object’s values just for the binding inside this -transient. - - • LEVEL is the suffix level, an integer between 1 and 7. See *note - Enabling and Disabling Suffixes::. - - • KEY is the key binding, either a vector or key description string. - - • DESCRIPTION is the description, either a string or a function that - takes zero or one arguments (the suffix object) and returns a - string. The function should be a lambda expression to avoid - ambiguity. In some cases a symbol that is bound as a function - would also work but to be safe you should use ‘:description’ in - that case. - - The next element is either a command or an argument. This is the -only argument that is mandatory in all cases. - - • COMMAND should be a symbol that is bound as a function, which has - to be defined or at least autoloaded as a command by the time the - containing prefix command is invoked. - - Any command will do; it does not need to have an object associated - with it (as would be the case if ‘transient-define-suffix’ or - ‘transient-define-infix’ were used to define it). - - COMMAND can also be a ‘lambda’ expression. - - As mentioned above, the object that is associated with a command - can be used to set the default for certain values that otherwise - have to be set in the suffix specification. Therefore if there is - no object, then you have to make sure to specify the KEY and the - DESCRIPTION. - - As a special case, if you want to add a command that might be - neither defined nor autoloaded, you can use a workaround like: - - (transient-insert-suffix 'some-prefix "k" - '("!" "Ceci n'est pas une commande" no-command - :if (lambda () (featurep 'no-library)))) - - Instead of ‘featurep’ you could also use ‘require’ with a non-‘nil’ - value for NOERROR. - - • The mandatory argument can also be a command-line argument, a - string. In that case an anonymous command is defined and bound. - - Instead of a string, this can also be a list of two strings, in - which case the first string is used as the short argument (which - can also be specified using ‘:shortarg’) and the second as the long - argument (which can also be specified using ‘:argument’). - - Only the long argument is displayed in the popup buffer. See - ‘transient-detect-key-conflicts’ for how the short argument may be - used. - - Unless the class is specified explicitly, the appropriate class is - guessed based on the long argument. If the argument ends with ‘=’ - (e.g., ‘--format=’) then ‘transient-option’ is used, otherwise - ‘transient-switch’. - - Finally, details can be specified using optional KEYWORD-VALUE pairs. -Each keyword has to be a keyword symbol, either ‘:class’ or a keyword -argument supported by the constructor of that class. See *note Suffix -Slots::. - - -File: transient.info, Node: Defining Suffix and Infix Commands, Next: Using Infix Arguments, Prev: Binding Suffix and Infix Commands, Up: Defining New Commands - -4.4 Defining Suffix and Infix Commands -====================================== - -Note that an infix is a special kind of suffix. Depending on context -“suffixes” means “suffixes (including infixes)” or “non-infix suffixes”. - - -- Macro: transient-define-suffix name arglist [docstring] [keyword - value]... body... - This macro defines NAME as a transient suffix command. - - ARGLIST are the arguments that the command takes. DOCSTRING is the - documentation string and is optional. - - These arguments can optionally be followed by keyword-value pairs. - Each keyword has to be a keyword symbol, either ‘:class’ or a - keyword argument supported by the constructor of that class. The - ‘transient-suffix’ class is used if the class is not specified - explicitly. - - The BODY must begin with an ‘interactive’ form that matches - ARGLIST. The infix arguments are usually accessed by using - ‘transient-args’ inside ‘interactive’. - - -- Macro: transient-define-infix name arglist [docstring] [keyword - value]... - This macro defines NAME as a transient infix command. - - ARGLIST is always ignored (but mandatory never-the-less) and - reserved for future use. DOCSTRING is the documentation string and - is optional. - - At least one key-value pair is required. All transient infix - commands are ‘equal’ to each other (but not ‘eq’). It is - meaningless to define an infix command, without providing at least - one keyword argument (usually ‘:argument’ or ‘:variable’, depending - on the class). The suffix class defaults to ‘transient-switch’ and - can be set using the ‘:class’ keyword. - - The function definition is always: - - (lambda () - (interactive) - (let ((obj (transient-suffix-object))) - (transient-infix-set obj (transient-infix-read obj))) - (transient--show)) - - ‘transient-infix-read’ and ‘transient-infix-set’ are generic - functions. Different infix commands behave differently because the - concrete methods are different for different infix command classes. - In rare cases the above command function might not be suitable, - even if you define your own infix command class. In that case you - have to use ‘transient-define-suffix’ to define the infix command - and use ‘t’ as the value of the ‘:transient’ keyword. - - -- Macro: transient-define-argument name arglist [docstring] [keyword - value]... - This macro defines NAME as a transient infix command. - - This is an alias for ‘transient-define-infix’. Only use this alias - to define an infix command that actually sets an infix argument. - To define an infix command that, for example, sets a variable, use - ‘transient-define-infix’ instead. - - -File: transient.info, Node: Using Infix Arguments, Next: Transient State, Prev: Defining Suffix and Infix Commands, Up: Defining New Commands - -4.5 Using Infix Arguments -========================= - -The functions and the variables described below allow suffix commands to -access the value of the transient from which they were invoked; which is -the value of its infix arguments. These variables are set when the user -invokes a suffix command that exits the transient, but before actually -calling the command. - - When returning to the command-loop after calling the suffix command, -the arguments are reset to ‘nil’ (which causes the function to return -‘nil’ too). - - Like for Emacs’ prefix arguments, it is advisable, but not mandatory, -to access the infix arguments inside the command’s ‘interactive’ form. -The preferred way of doing that is to call the ‘transient-args’ -function, which for infix arguments serves about the same purpose as -‘prefix-arg’ serves for prefix arguments. - - -- Function: transient-args prefix - This function returns the value of the transient prefix command - PREFIX. - - If the current command was invoked from the transient prefix - command PREFIX, then it returns the active infix arguments. If the - current command was not invoked from PREFIX, then it returns the - set, saved or default value for PREFIX. - - -- Function: transient-arg-value arg args - This function return the value of ARG as it appears in ARGS. - - For a switch a boolean is returned. For an option the value is - returned as a string, using the empty string for the empty value, - or ‘nil’ if the option does not appear in ARGS. - - -- Function: transient-suffixes prefix - This function returns the suffixes of the transient prefix command - PREFIX. This is a list of objects. This function should only be - used if you need the objects (as opposed to just their values) and - if the current command is not being invoked from PREFIX. - - -- Variable: transient-current-suffixes - The suffixes of the transient from which this suffix command was - invoked. This is a list of objects. Usually it is sufficient to - instead use the function ‘transient-args’, which returns a list of - values. In complex cases it might be necessary to use this - variable instead, i.e., if you need access to information beside - the value. - - -- Variable: transient-current-prefix - The transient from which this suffix command was invoked. The - returned value is a ‘transient-prefix’ object, which holds - information associated with the transient prefix command. - - -- Variable: transient-current-command - The transient from which this suffix command was invoked. The - returned value is a symbol, the transient prefix command. - - -File: transient.info, Node: Transient State, Prev: Using Infix Arguments, Up: Defining New Commands - -4.6 Transient State -=================== - -Invoking a transient prefix command “activates” the respective -transient, i.e., it puts a transient keymap into effect, which binds the -transient’s infix and suffix commands. - - The default behavior while a transient is active is as follows: - - • Invoking an infix command does not affect the transient state; the - transient remains active. - - • Invoking a (non-infix) suffix command “deactivates” the transient - state by removing the transient keymap and performing some - additional cleanup. - - • Invoking a command that is bound in a keymap other than the - transient keymap is disallowed and trying to do so results in a - warning. This does not “deactivate” the transient. - - The behavior can be changed for all suffixes of a particular prefix -and/or for individual suffixes. The values should nearly always be -booleans, but certain functions, called “pre-commands”, can also be -used. These functions are named ‘transient--do-VERB’, and the symbol -‘VERB’ can be used as a shorthand. - - A boolean is interpreted as answering the question "does the -transient stay active, when this command is invoked?" ‘t’ means that -the transient stays active, while ‘nil’ means that invoking the command -exits the transient. - - Note that when the suffix is a “sub-prefix”, invoking that command -always activates that sub-prefix, causing the outer prefix to no longer -be active and displayed. Here ‘t’ means that when you exit the inner -prefix, then the outer prefix becomes active again, while ‘nil’ means -that all outer prefixes are exited at once. - - • The behavior for non-suffixes can be set for a particular prefix, - by the prefix’s ‘transient-non-suffix’ slot to a boolean, a - suitable pre-command function, or a shorthand for such a function. - See *note Pre-commands for Non-Suffixes::. - - • The common behavior for the suffixes of a particular prefix can be - set using the prefix’s ‘transient-suffixes’ slot. - - The value specified in this slot does *not* affect infixes. - Because it affects both regular suffixes as well as sub-prefixes, - which have different needs, it is best to avoid explicitly - specifying a function. - - • The behavior of an individual suffix can be changed using its - ‘transient’ slot. While it is usually best to use a boolean, for - this slot it can occasionally make sense to specify a function - explicitly. - - Note that this slot can be set when defining a suffix command using - ‘transient-define-suffix’ and/or in the definition of the prefix. - If set in both places, then the latter takes precedence, as usual. - - The available pre-command functions are documented in the following -sub-sections. They are called by ‘transient--pre-command’, a function -on ‘pre-command-hook’, and the value that they return determines whether -the transient is exited. To do so the value of one of the constants -‘transient--exit’ or ‘transient--stay’ is used (that way we don’t have -to remember if ‘t’ means “exit” or “stay”). - - Additionally, these functions may change the value of ‘this-command’ -(which explains why they have to be called using ‘pre-command-hook’), -call ‘transient-export’, ‘transient--stack-zap’ or -‘transient--stack-push’; and set the values of ‘transient--exitp’, -‘transient--helpp’ or ‘transient--editp’. - - For completeness sake, some notes about complications: - - • The transient-ness of certain built-in suffix commands is specified - using ‘transient-predicate-map’. This is a special keymap, which - binds commands to pre-commands (as opposed to keys to commands) and - takes precedence over the prefix’s ‘transient-suffix’ slot, but not - the suffix’s ‘transient’ slot. - - • While a sub-prefix is active we nearly always want ‘C-g’ to take - the user back to the “super-prefix”, even when the other suffixes - don’t do that. However, in rare cases this may not be desirable, - and that makes the following complication necessary: - - For ‘transient-suffix’ objects the ‘transient’ slot is unbound. We - can ignore that for the most part because ‘nil’ and the slot being - unbound are treated as equivalent, and mean “do exit”. That isn’t - actually true for suffixes that are sub-prefixes though. For such - suffixes unbound means “do exit but allow going back”, which is the - default, while ‘nil’ means “do exit permanently”, which requires - that slot to be explicitly set to that value. - -Pre-commands for Infixes ------------------------- - -The default for infixes is ‘transient--do-stay’. This is also the only -function that makes sense for infixes, which is why this predicate is -used even if the value of the prefix’s ‘transient-suffix’ slot is ‘t’. -In extremely rare cases, one might want to use something else, which can -be done by setting the infix’s ‘transient’ slot directly. - - -- Function: transient--do-stay - Call the command without exporting variables and stay transient. - -Pre-commands for Suffixes -------------------------- - -By default, invoking a suffix causes the transient to be exited. - - The behavior for an individual suffix command can be changed by -setting its ‘transient’ slot to a boolean (which is highly recommended), -or to one of the following pre-commands. - - -- Function: transient--do-exit - Call the command after exporting variables and exit the transient. - - -- Function: transient--do-return - Call the command after exporting variables and return to the parent - prefix. If there is no parent prefix, then call - ‘transient--do-exit’. - - -- Function: transient--do-call - Call the command after exporting variables and stay transient. - - The following pre-commands are only suitable for sub-prefixes. It is -not necessary to explicitly use these predicates because the correct -predicate is automatically picked based on the value of the ‘transient’ -slot for the sub-prefix itself. - - -- Function: transient--do-recurse - Call the transient prefix command, preparing for return to active - transient. - - Whether we actually return to the parent transient is ultimately - under the control of each invoked suffix. The difference between - this pre-command and ‘transient--do-stack’ is that it changes the - value of the ‘transient-suffix’ slot to ‘t’. - - If there is no parent transient, then only call this command and - skip the second step. - - -- Function: transient--do-stack - Call the transient prefix command, stacking the active transient. - Push the active transient to the transient stack. - - Unless ‘transient--do-recurse’ is explicitly used, this pre-command - is automatically used for suffixes that are prefixes themselves, - i.e., for sub-prefixes. - - -- Function: transient--do-replace - Call the transient prefix command, replacing the active transient. - Do not push the active transient to the transient stack. - - Unless ‘transient--do-recurse’ is explicitly used, this pre-command - is automatically used for suffixes that are prefixes themselves, - i.e., for sub-prefixes. - - -- Function: transient--do-suspend - Suspend the active transient, saving the transient stack. - - This is used by the command ‘transient-suspend’ and optionally also - by “external events” such as ‘handle-switch-frame’. Such bindings - should be added to ‘transient-predicate-map’. - -Pre-commands for Non-Suffixes ------------------------------ - -By default, non-suffixes (commands that are bound in other keymaps -beside the transient keymap) cannot be invoked. Trying to invoke such a -command results in a warning and the transient stays active. - - If you want a different behavior, then set the ‘transient-non-suffix’ -slot of the transient prefix command. The value should be a boolean, -answering the question, "is it allowed to invoke non-suffix commands?, a -pre-command function, or a shorthand for such a function. - - If the value is ‘t’, then non-suffixes can be invoked, when it is -‘nil’ (the default) then they cannot be invoked. - - The only other recommended value is ‘leave’. If that is used, then -non-suffixes can be invoked, but if one is invoked, then that exits the -transient. - - -- Function: transient--do-warn - Call ‘transient-undefined’ and stay transient. - - -- Function: transient--do-stay - Call the command without exporting variables and stay transient. - - -- Function: transient--do-leave - Call the command without exporting variables and exit the - transient. - -Special Pre-Commands --------------------- - - -- Function: transient--do-quit-one - If active, quit help or edit mode, else exit the active transient. - - This is used when the user pressed ‘C-g’. - - -- Function: transient--do-quit-all - Exit all transients without saving the transient stack. - - This is used when the user pressed ‘C-q’. - - -- Function: transient--do-suspend - Suspend the active transient, saving the transient stack. - - This is used when the user pressed ‘C-z’. - - -File: transient.info, Node: Classes and Methods, Next: FAQ, Prev: Defining New Commands, Up: Top - -5 Classes and Methods -********************* - -Transient uses classes and generic functions to make it possible to -define new types of suffix commands that are similar to existing types, -but behave differently in some aspects. It does the same for groups and -prefix commands, though at least for prefix commands that *currently* -appears to be less important. - - Every prefix, infix and suffix command is associated with an object, -which holds information that controls certain aspects of its behavior. -This happens in two ways. - - • Associating a command with a certain class gives the command a - type. This makes it possible to use generic functions to do - certain things that have to be done differently depending on what - type of command it acts on. - - That in turn makes it possible for third-parties to add new types - without having to convince the maintainer of Transient that that - new type is important enough to justify adding a special case to a - dozen or so functions. - - • Associating a command with an object makes it possible to easily - store information that is specific to that particular command. - - Two commands may have the same type, but obviously their key - bindings and descriptions still have to be different, for example. - - The values of some slots are functions. The ‘reader’ slot for - example holds a function that is used to read a new value for an - infix command. The values of such slots are regular functions. - - Generic functions are used when a function should do something - different based on the type of the command, i.e., when all commands - of a certain type should behave the same way but different from the - behavior for other types. Object slots that hold a regular - function as value are used when the task that they perform is - likely to differ even between different commands of the same type. - -* Menu: - -* Group Classes:: -* Group Methods:: -* Prefix Classes:: -* Suffix Classes:: -* Suffix Methods:: -* Prefix Slots:: -* Suffix Slots:: -* Predicate Slots:: - - -File: transient.info, Node: Group Classes, Next: Group Methods, Up: Classes and Methods - -5.1 Group Classes -================= - -The type of a group can be specified using the ‘:class’ property at the -beginning of the class specification, e.g., ‘[:class transient-columns -...]’ in a call to ‘transient-define-prefix’. - - • The abstract ‘transient-child’ class is the base class of both - ‘transient-group’ (and therefore all groups) as well as of - ‘transient-suffix’ (and therefore all suffix and infix commands). - - This class exists because the elements (or “children”) of certain - groups can be other groups instead of suffix and infix commands. - - • The abstract ‘transient-group’ class is the superclass of all other - group classes. - - • The ‘transient-column’ class is the simplest group. - - This is the default “flat” group. If the class is not specified - explicitly and the first element is not a vector (i.e., not a - group), then this class is used. - - This class displays each element on a separate line. - - • The ‘transient-row’ class displays all elements on a single line. - - • The ‘transient-columns’ class displays commands organized in - columns. - - Direct elements have to be groups whose elements have to be - commands or strings. Each subgroup represents a column. This - class takes care of inserting the subgroups’ elements. - - This is the default “nested” group. If the class is not specified - explicitly and the first element is a vector (i.e., a group), then - this class is used. - - • The ‘transient-subgroups’ class wraps other groups. - - Direct elements have to be groups whose elements have to be - commands or strings. This group inserts an empty line between - subgroups. The subgroups themselves are responsible for displaying - their elements. - - -File: transient.info, Node: Group Methods, Next: Prefix Classes, Prev: Group Classes, Up: Classes and Methods - -5.2 Group Methods -================= - - -- Function: transient-setup-children group children - This generic function can be used to setup the children or a group. - - The default implementation usually just returns the children - unchanged, but if the ‘setup-children’ slot of GROUP is non-‘nil’, - then it calls that function with CHILDREN as the only argument and - returns the value. - - The children are given as a (potentially empty) list consisting of - either group or suffix specifications. These functions can make - arbitrary changes to the children including constructing new - children from scratch. - - -- Function: transient--insert-group group - This generic function formats the group and its elements and - inserts the result into the current buffer, which is a temporary - buffer. The contents of that buffer are later inserted into the - popup buffer. - - Functions that are called by this function may need to operate in - the buffer from which the transient was called. To do so they can - temporarily make the ‘transient--source-buffer’ the current buffer. - - -File: transient.info, Node: Prefix Classes, Next: Suffix Classes, Prev: Group Methods, Up: Classes and Methods - -5.3 Prefix Classes -================== - -Currently the ‘transient-prefix’ class is being used for all prefix -commands and there is only a single generic function that can be -specialized based on the class of a prefix command. - - -- Function: transient--history-init obj - This generic function is called while setting up the transient and - is responsible for initializing the ‘history’ slot. This is the - transient-wide history; many individual infixes also have a history - of their own. - - The default (and currently only) method extracts the value from the - global variable ‘transient-history’. - - A transient prefix command’s object is stored in the -‘transient--prefix’ property of the command symbol. While a transient -is active, a clone of that object is stored in the variable -‘transient--prefix’. A clone is used because some changes that are made -to the active transient’s object should not affect later invocations. - - -File: transient.info, Node: Suffix Classes, Next: Suffix Methods, Prev: Prefix Classes, Up: Classes and Methods - -5.4 Suffix Classes -================== - - • All suffix and infix classes derive from ‘transient-suffix’, which - in turn derives from ‘transient-child’, from which - ‘transient-group’ also derives (see *note Group Classes::). - - • All infix classes derive from the abstract ‘transient-infix’ class, - which in turn derives from the ‘transient-suffix’ class. - - Infixes are a special type of suffixes. The primary difference is - that infixes always use the ‘transient--do-stay’ pre-command, while - non-infix suffixes use a variety of pre-commands (see *note - Transient State::). Doing that is most easily achieved by using - this class, though theoretically it would be possible to define an - infix class that does not do so. If you do that then you get to - implement many methods. - - Also, infixes and non-infix suffixes are usually defined using - different macros (see *note Defining Suffix and Infix Commands::). - - • Classes used for infix commands that represent arguments should be - derived from the abstract ‘transient-argument’ class. - - • The ‘transient-switch’ class (or a derived class) is used for infix - arguments that represent command-line switches (arguments that do - not take a value). - - • The ‘transient-option’ class (or a derived class) is used for infix - arguments that represent command-line options (arguments that do - take a value). - - • The ‘transient-switches’ class can be used for a set of mutually - exclusive command-line switches. - - • The ‘transient-files’ class can be used for a ‘--’ argument that - indicates that all remaining arguments are files. - - • Classes used for infix commands that represent variables should - derived from the abstract ‘transient-variable’ class. - - • The ‘transient-information’ class is special in that suffixes that - use this class are not associated with a command and thus also not - with any key binding. Such suffixes are only used to display - arbitrary information, and that anywhere a suffix can appear. - Display-only suffix specifications take this form: - - ([LEVEL] :info DESCRIPTION [KEYWORD VALUE]...) - - The ‘:info’ keyword argument replaces the ‘:description’ keyword - used for other suffix classes. Other keyword arguments that you - might want to set, include ‘:face’, predicate keywords (such as - ‘:if’), and ‘:format’. By default the value of ‘:format’ includes - ‘%k’, which for this class is replaced with the empty string or - spaces, if keys are being padded in the containing group. - - Magit defines additional classes, which can serve as examples for the -fancy things you can do without modifying Transient. Some of these -classes will likely get generalized and added to Transient. For now -they are very much subject to change and not documented. - - -File: transient.info, Node: Suffix Methods, Next: Prefix Slots, Prev: Suffix Classes, Up: Classes and Methods - -5.5 Suffix Methods -================== - -To get information about the methods implementing these generic -functions use ‘describe-function’. - -* Menu: - -* Suffix Value Methods:: -* Suffix Format Methods:: - - -File: transient.info, Node: Suffix Value Methods, Next: Suffix Format Methods, Up: Suffix Methods - -5.5.1 Suffix Value Methods --------------------------- - - -- Function: transient-init-value obj - This generic function sets the initial value of the object OBJ. - - This function is called for all suffix commands, but unless a - concrete method is implemented this falls through to the default - implementation, which is a noop. In other words this usually only - does something for infix commands, but note that this is not - implemented for the abstract class ‘transient-infix’, so if your - class derives from that directly, then you must implement a method. - - -- Function: transient-infix-read obj - This generic function determines the new value of the infix object - OBJ. - - This function merely determines the value; ‘transient-infix-set’ is - used to actually store the new value in the object. - - For most infix classes this is done by reading a value from the - user using the reader specified by the ‘reader’ slot (using the - ‘transient-infix-value’ method described below). - - For some infix classes the value is changed without reading - anything in the minibuffer, i.e., the mere act of invoking the - infix command determines what the new value should be, based on the - previous value. - - -- Function: transient-prompt obj - This generic function returns the prompt to be used to read infix - object OBJ’s value. - - -- Function: transient-infix-set obj value - This generic function sets the value of infix object OBJ to VALUE. - - -- Function: transient-infix-value obj - This generic function returns the value of the suffix object OBJ. - - This function is called by ‘transient-args’ (which see), meaning - this function is how the value of a transient is determined so that - the invoked suffix command can use it. - - Currently most values are strings, but that is not set in stone. - ‘nil’ is not a value, it means “no value”. - - Usually only infixes have a value, but see the method for - ‘transient-suffix’. - - -- Function: transient-init-scope obj - This generic function sets the scope of the suffix object OBJ. - - The scope is actually a property of the transient prefix, not of - individual suffixes. However it is possible to invoke a suffix - command directly instead of from a transient. In that case, if the - suffix expects a scope, then it has to determine that itself and - store it in its ‘scope’ slot. - - This function is called for all suffix commands, but unless a - concrete method is implemented this falls through to the default - implementation, which is a noop. - - -File: transient.info, Node: Suffix Format Methods, Prev: Suffix Value Methods, Up: Suffix Methods - -5.5.2 Suffix Format Methods ---------------------------- - - -- Function: transient-format obj - This generic function formats and returns OBJ for display. - - When this function is called, then the current buffer is some - temporary buffer. If you need the buffer from which the prefix - command was invoked to be current, then do so by temporarily making - ‘transient--source-buffer’ current. - - -- Function: transient-format-key obj - This generic function formats OBJ’s ‘key’ for display and returns - the result. - - -- Function: transient-format-description obj - This generic function formats OBJ’s ‘description’ for display and - returns the result. - - -- Function: transient-format-value obj - This generic function formats OBJ’s value for display and returns - the result. - - -- Function: transient-show-help obj - Show help for the prefix, infix or suffix command represented by - OBJ. - - For prefixes, show the info manual, if that is specified using the - ‘info-manual’ slot. Otherwise, show the manpage if that is - specified using the ‘man-page’ slot. Otherwise, show the command’s - documentation string. - - For suffixes, show the command’s documentation string. - - For infixes, show the manpage if that is specified. Otherwise show - the command’s documentation string. - - -File: transient.info, Node: Prefix Slots, Next: Suffix Slots, Prev: Suffix Methods, Up: Classes and Methods - -5.6 Prefix Slots -================ - - • ‘show-help’, ‘man-page’ or ‘info-manual’ can be used to specify the - documentation for the prefix and its suffixes. The command - ‘transient-help’ uses the method ‘transient-show-help’ (which see) - to lookup and use these values. - - • ‘history-key’ If multiple prefix commands should share a single - value, then this slot has to be set to the same value for all of - them. You probably don’t want that. - - • ‘transient-suffix’ and ‘transient-non-suffix’ play a part when - determining whether the currently active transient prefix command - remains active/transient when a suffix or arbitrary non-suffix - command is invoked. See *note Transient State::. - - • ‘refresh-suffixes’ Normally suffix objects and keymaps are only - setup once, when the prefix is invoked. Setting this to ‘t’, - causes them to be recreated after every command. This is useful - when using ‘:if...’ predicates, and those need to be rerun for some - reason. Doing this is somewhat costly, and there is a risk of - losing state, so this is disabled by default and still considered - experimental. - - • ‘incompatible’ A list of lists. Each sub-list specifies a set of - mutually exclusive arguments. Enabling one of these arguments - causes the others to be disabled. An argument may appear in - multiple sub-lists. Arguments must me given in the same form as - used in the ‘argument’ or ‘argument-format’ slot of the respective - suffix objects, usually something like ‘--switch’ or ‘--option=%s’. - For options and ‘transient-switches’ suffixes it is also possible - to match against a specific value, as returned by - ‘transient-infix-value’, for example, ‘--option=one’. - - • ‘scope’ For some transients it might be necessary to have a sort of - secondary value, called a “scope”. See ‘transient-define-prefix’. - -Internal Prefix Slots ---------------------- - -These slots are mostly intended for internal use. They should not be -set in calls to ‘transient-define-prefix’. - - • ‘prototype’ When a transient prefix command is invoked, then a - clone of that object is stored in the global variable - ‘transient--prefix’ and the prototype is stored in the clone’s - ‘prototype’ slot. - - • ‘command’ The command, a symbol. Each transient prefix command - consists of a command, which is stored in a symbol’s function slot - and an object, which is stored in the ‘transient--prefix’ property - of the same symbol. - - • ‘level’ The level of the prefix commands. The suffix commands - whose layer is equal or lower are displayed. See *note Enabling - and Disabling Suffixes::. - - • ‘value’ The likely outdated value of the prefix. Instead of - accessing this slot directly you should use the function - ‘transient-get-value’, which is guaranteed to return the up-to-date - value. - - • ‘history’ and ‘history-pos’ are used to keep track of historic - values. Unless you implement your own ‘transient-infix-read’ - method you should not have to deal with these slots. - - -File: transient.info, Node: Suffix Slots, Next: Predicate Slots, Prev: Prefix Slots, Up: Classes and Methods - -5.7 Suffix Slots -================ - -Here we document most of the slots that are only available for suffix -objects. Some slots are shared by suffix and group objects, they are -documented in *note Predicate Slots::. - - Also see *note Suffix Classes::. - -Slots of ‘transient-suffix’ ---------------------------- - - • ‘key’ The key, a key vector or a key description string. - - • ‘command’ The command, a symbol. - - • ‘transient’ Whether to stay transient. See *note Transient - State::. - - • ‘format’ The format used to display the suffix in the popup buffer. - It must contain the following %-placeholders: - - • ‘%k’ For the key. - • ‘%d’ For the description. - • ‘%v’ For the infix value. Non-infix suffixes don’t have a - value. - - • ‘description’ The description, either a string or a function, which - is called with zero or one argument (the suffix object), and - returns a string. - - • ‘face’ Face used for the description. In simple cases it is easier - to use this instead of using a function as ‘description’ and adding - the styling there. ‘face’ is appended using - ‘add-face-text-property’. - - • ‘show-help’ A function used to display help for the suffix. If - unspecified, the prefix controls how help is displayed for its - suffixes. - -Slots of ‘transient-infix’ --------------------------- - -Some of these slots are only meaningful for some of the subclasses. -They are defined here anyway to allow sharing certain methods. - - • ‘argument’ The long argument, e.g., ‘--verbose’. - - • ‘shortarg’ The short argument, e.g., ‘-v’. - - • ‘value’ The value. Should not be accessed directly. - - • ‘init-value’ Function that is responsible for setting the object’s - value. If bound, then this is called with the object as the only - argument. Usually this is not bound, in which case the object’s - primary ‘transient-init-value’ method is called instead. - - • ‘unsavable’ Whether the value of the suffix is not saved as part of - the prefixes. - - • ‘multi-value’ For options, whether the option can have multiple - values. If this is non-‘nil’, then the values are read using - ‘completing-read-multiple’ by default and if you specify your own - reader, then it should read the values using that function or - similar. - - Supported non-‘nil’ values are: - - • Use ‘rest’ for an option that can have multiple values. This - is useful e.g., for an ‘--’ argument that indicates that all - remaining arguments are files (such as ‘git log -- file1 - file2’). - - In the list returned by ‘transient-args’ such an option and - its values are represented by a single list of the form - ‘(ARGUMENT . VALUES)’. - - • Use ‘repeat’ for an option that can be specified multiple - times. - - In the list returned by ‘transient-args’ each instance of the - option and its value appears separately in the usual from, for - example: ‘("--another-argument" "--option=first" - "--option=second")’. - - In both cases the option’s values have to be specified in the - default value of a prefix using the same format as returned by - ‘transient-args’, e.g., ‘("--other" "--o=1" "--o=2" ("--" "f1" - "f2"))’. - - • ‘always-read’ For options, whether to read a value on every - invocation. If this is ‘nil’, then options that have a value are - simply unset and have to be invoked a second time to set a new - value. - - • ‘allow-empty’ For options, whether the empty string is a valid - value. - - • ‘history-key’ The key used to store the history. This defaults to - the command name. This is useful when multiple infixes should - share the same history because their values are of the same kind. - - • ‘reader’ The function used to read the value of an infix. Not used - for switches. The function takes three arguments, PROMPT, - INITIAL-INPUT and HISTORY, and must return a string. - - • ‘prompt’ The prompt used when reading the value, either a string or - a function that takes the object as the only argument and which - returns a prompt string. - - • ‘choices’ A list of valid values, or a function that returns such a - list. The latter is not implemented for ‘transient-switches’, - because I couldn’t think of a use-case. How exactly the choices - are used varies depending on the class of the suffix. - -Slots of ‘transient-variable’ ------------------------------ - - • ‘variable’ The variable. - -Slots of ‘transient-switches’ ------------------------------ - - • ‘argument-format’ The display format. Must contain ‘%s’, one of - the ‘choices’ is substituted for that. E.g., ‘--%s-order’. - - • ‘argument-regexp’ The regexp used to match any one of the switches. - E.g., ‘\\(--\\(topo\\|author-date\\|date\\)-order\\)’. - - -File: transient.info, Node: Predicate Slots, Prev: Suffix Slots, Up: Classes and Methods - -5.8 Predicate Slots -=================== - -Suffix and group objects share some predicate slots that control whether -a group or suffix should be available depending on some state. Only one -of these slots can be used at the same time. It is undefined what -happens if you use more than one. - - • ‘if’ Enable if predicate returns non-‘nil’. - • ‘if-not’ Enable if predicate returns ‘nil’. - • ‘if-non-nil’ Enable if variable’s value is non-‘nil’. - • ‘if-nil’ Enable if variable’s value is ‘nil’. - • ‘if-mode’ Enable if major-mode matches value. - • ‘if-not-mode’ Enable if major-mode does not match value. - • ‘if-derived’ Enable if major-mode derives from value. - • ‘if-not-derived’ Enable if major-mode does not derive from value. - - By default these predicates run when the prefix command is invoked, -but this can be changes, using the ‘refresh-suffixes’ prefix slot. See -*note Prefix Slots::. - - One more slot is shared between group and suffix classes, ‘level’. -Like the slots documented above, it is a predicate, but it is used for a -different purpose. The value has to be an integer between 1 and 7. -‘level’ controls whether a suffix or a group should be available -depending on user preference. See *note Enabling and Disabling -Suffixes::. - - -File: transient.info, Node: FAQ, Next: Keystroke Index, Prev: Classes and Methods, Up: Top - -Appendix A FAQ -************** - -A.1 Can I control how the popup buffer is displayed? -==================================================== - -Yes, see ‘transient-display-buffer-action’ in *note Configuration::. - -A.2 How can I copy text from the popup buffer? -============================================== - -To be able to mark text in Transient’s popup buffer using the mouse, you -have to add the below binding. Note that for technical reasons, the -region won’t be visualized, while doing so. After you have quit the -transient popup, you will be able to yank it in another buffer. - - (keymap-set transient-predicate-map - "<mouse-set-region>" - #'transient--do-stay) - -A.3 How does Transient compare to prefix keys and universal arguments? -====================================================================== - -See -<https://github.com/magit/transient/wiki/Comparison-with-prefix-keys-and-universal-arguments>. - -A.4 How does Transient compare to Magit-Popup and Hydra? -======================================================== - -See -<https://github.com/magit/transient/wiki/Comparison-with-other-packages>. - -A.5 Why did some of the key bindings change? -============================================ - -You may have noticed that the bindings for some of the common commands -do *not* have the prefix ‘C-x’ and that furthermore some of these -commands are grayed out while others are not. That unfortunately is a -bit confusing if the section of common commands is not shown -permanently, making the following explanation necessary. - - The purpose of usually hiding that section but showing it after the -user pressed the respective prefix key is to conserve space and not -overwhelm users with too much noise, while allowing the user to quickly -list common bindings on demand. - - That however should not keep us from using the best possible key -bindings. The bindings that do use a prefix do so to avoid wasting too -many non-prefix bindings, keeping them available for use in individual -transients. The bindings that do not use a prefix and that are *not* -grayed out are very important bindings that are *always* available, even -when invoking the “common command key prefix” or *any other* -transient-specific prefix. The non-prefix keys that *are* grayed out -however, are not available when any incomplete prefix key sequence is -active. They do not use the “common command key prefix” because it is -likely that users want to invoke them several times in a row and e.g., -‘M-p M-p M-p’ is much more convenient than ‘C-x M-p C-x M-p C-x M-p’. - - You may also have noticed that the “Set” command is bound to ‘C-x s’, -while Magit-Popup used to bind ‘C-c C-c’ instead. I have seen several -users praise the latter binding (sic), so I did not change it -willy-nilly. The reason that I changed it is that using different -prefix keys for different common commands, would have made the temporary -display of the common commands even more confusing, i.e., after pressing -‘C-c’ all the bindings that begin with the ‘C-x’ prefix would be grayed -out. - - Using a single prefix for common commands key means that all other -potential prefix keys can be used for transient-specific commands -*without* the section of common commands also popping up. ‘C-c’ in -particular is a prefix that I want to (and already do) use for Magit, -and also using that for a common command would prevent me from doing so. - - (Also see the next question.) - -A.6 Why does ‘q’ not quit popups anymore? -========================================= - -I agree that ‘q’ is a good binding for commands that quit something. -This includes quitting whatever transient is currently active, but it -also includes quitting whatever it is that some specific transient is -controlling. The transient ‘magit-blame’ for example binds ‘q’ to the -command that turns ‘magit-blame-mode’ off. - - So I had to decide if ‘q’ should quit the active transient (like -Magit-Popup used to) or whether ‘C-g’ should do that instead, so that -‘q’ could be bound in individual transient to whatever commands make -sense for them. Because all other letters are already reserved for use -by individual transients, I have decided to no longer make an exception -for ‘q’. - - If you want to get ‘q’’s old binding back then you can do so. Doing -that is a bit more complicated than changing a single key binding, so I -have implemented a function, ‘transient-bind-q-to-quit’ that makes the -necessary changes. See its documentation string for more information. - - -File: transient.info, Node: Keystroke Index, Next: Command and Function Index, Prev: FAQ, Up: Top - -Appendix B Keystroke Index -************************** - - -* Menu: - -* C-g: Aborting and Resuming Transients. - (line 27) -* C-g <1>: Aborting and Resuming Transients. - (line 27) -* C-h: Getting Help for Suffix Commands. - (line 11) -* C-M-n: Using History. (line 18) -* C-M-p: Using History. (line 13) -* C-q: Aborting and Resuming Transients. - (line 36) -* C-x a: Enabling and Disabling Suffixes. - (line 68) -* C-x C-k: Saving Values. (line 29) -* C-x C-s: Saving Values. (line 25) -* C-x l: Enabling and Disabling Suffixes. - (line 43) -* C-x n: Using History. (line 18) -* C-x p: Using History. (line 13) -* C-x s: Saving Values. (line 21) -* C-x t: Common Suffix Commands. - (line 18) -* C-z: Aborting and Resuming Transients. - (line 41) - - -File: transient.info, Node: Command and Function Index, Next: Variable Index, Prev: Keystroke Index, Up: Top - -Appendix C Command and Function Index -************************************* - - -* Menu: - -* transient--do-call: Transient State. (line 125) -* transient--do-exit: Transient State. (line 117) -* transient--do-leave: Transient State. (line 193) -* transient--do-quit-all: Transient State. (line 205) -* transient--do-quit-one: Transient State. (line 200) -* transient--do-recurse: Transient State. (line 133) -* transient--do-replace: Transient State. (line 153) -* transient--do-return: Transient State. (line 120) -* transient--do-stack: Transient State. (line 145) -* transient--do-stay: Transient State. (line 105) -* transient--do-stay <1>: Transient State. (line 190) -* transient--do-suspend: Transient State. (line 161) -* transient--do-suspend <1>: Transient State. (line 210) -* transient--do-warn: Transient State. (line 187) -* transient--history-init: Prefix Classes. (line 10) -* transient--insert-group: Group Methods. (line 19) -* transient-append-suffix: Modifying Existing Transients. - (line 51) -* transient-arg-value: Using Infix Arguments. - (line 31) -* transient-args: Using Infix Arguments. - (line 22) -* transient-define-argument: Defining Suffix and Infix Commands. - (line 57) -* transient-define-infix: Defining Suffix and Infix Commands. - (line 26) -* transient-define-prefix: Defining Transients. (line 13) -* transient-define-suffix: Defining Suffix and Infix Commands. - (line 9) -* transient-format: Suffix Format Methods. - (line 6) -* transient-format-description: Suffix Format Methods. - (line 18) -* transient-format-key: Suffix Format Methods. - (line 14) -* transient-format-value: Suffix Format Methods. - (line 22) -* transient-get-suffix: Modifying Existing Transients. - (line 73) -* transient-help: Getting Help for Suffix Commands. - (line 11) -* transient-history-next: Using History. (line 18) -* transient-history-prev: Using History. (line 13) -* transient-infix-read: Suffix Value Methods. - (line 16) -* transient-infix-set: Suffix Value Methods. - (line 36) -* transient-infix-value: Suffix Value Methods. - (line 39) -* transient-init-scope: Suffix Value Methods. - (line 52) -* transient-init-value: Suffix Value Methods. - (line 6) -* transient-insert-suffix: Modifying Existing Transients. - (line 49) -* transient-prompt: Suffix Value Methods. - (line 32) -* transient-quit-all: Aborting and Resuming Transients. - (line 36) -* transient-quit-one: Aborting and Resuming Transients. - (line 27) -* transient-quit-seq: Aborting and Resuming Transients. - (line 27) -* transient-remove-suffix: Modifying Existing Transients. - (line 70) -* transient-replace-suffix: Modifying Existing Transients. - (line 66) -* transient-reset: Saving Values. (line 29) -* transient-resume: Aborting and Resuming Transients. - (line 53) -* transient-save: Saving Values. (line 25) -* transient-scroll-down: Other Commands. (line 17) -* transient-scroll-up: Other Commands. (line 12) -* transient-set: Saving Values. (line 21) -* transient-set-level: Enabling and Disabling Suffixes. - (line 43) -* transient-setup-children: Group Methods. (line 6) -* transient-show-help: Suffix Format Methods. - (line 26) -* transient-suffix-put: Modifying Existing Transients. - (line 77) -* transient-suffixes: Using Infix Arguments. - (line 38) -* transient-suspend: Aborting and Resuming Transients. - (line 41) -* transient-toggle-common: Common Suffix Commands. - (line 18) -* transient-toggle-level-limit: Enabling and Disabling Suffixes. - (line 68) - - -File: transient.info, Node: Variable Index, Next: Concept Index, Prev: Command and Function Index, Up: Top - -Appendix D Variable Index -************************* - - -* Menu: - -* transient-align-variable-pitch: Configuration. (line 185) -* transient-current-command: Using Infix Arguments. - (line 57) -* transient-current-prefix: Using Infix Arguments. - (line 52) -* transient-current-suffixes: Using Infix Arguments. - (line 44) -* transient-default-level: Enabling and Disabling Suffixes. - (line 33) -* transient-detect-key-conflicts: Configuration. (line 210) -* transient-display-buffer-action: Configuration. (line 51) -* transient-enable-popup-navigation: Configuration. (line 36) -* transient-force-fixed-pitch: Configuration. (line 198) -* transient-force-single-column: Configuration. (line 93) -* transient-hide-during-minibuffer-read: Configuration. (line 181) -* transient-highlight-higher-levels: Configuration. (line 223) -* transient-highlight-mismatched-keys: Configuration. (line 135) -* transient-history-file: Using History. (line 33) -* transient-history-limit: Using History. (line 37) -* transient-levels-file: Enabling and Disabling Suffixes. - (line 38) -* transient-mode-line-format: Configuration. (line 102) -* transient-read-with-initial-input: Configuration. (line 174) -* transient-semantic-coloring: Configuration. (line 126) -* transient-show-common-commands: Common Suffix Commands. - (line 23) -* transient-show-popup: Configuration. (line 15) -* transient-substitute-key-function: Configuration. (line 153) -* transient-values-file: Saving Values. (line 31) - - -File: transient.info, Node: Concept Index, Next: GNU General Public License, Prev: Variable Index, Up: Top - -Appendix E Concept Index -************************ - - -* Menu: - -* aborting transients: Aborting and Resuming Transients. - (line 6) -* classes and methods: Classes and Methods. (line 6) -* command dispatchers: Technical Introduction. - (line 39) -* common suffix commands: Common Suffix Commands. - (line 6) -* defining infix commands: Defining Suffix and Infix Commands. - (line 6) -* defining suffix commands: Defining Suffix and Infix Commands. - (line 6) -* disabling suffixes: Enabling and Disabling Suffixes. - (line 6) -* enabling suffixes: Enabling and Disabling Suffixes. - (line 6) -* getting help: Getting Help for Suffix Commands. - (line 6) -* group specifications: Group Specifications. (line 6) -* invoking transients: Invoking Transients. (line 6) -* levels: Enabling and Disabling Suffixes. - (line 10) -* modifying existing transients: Modifying Existing Transients. - (line 6) -* quit transient: Aborting and Resuming Transients. - (line 6) -* resuming transients: Aborting and Resuming Transients. - (line 6) -* saving values of arguments: Saving Values. (line 6) -* scope of a transient: Defining Transients. (line 43) -* suffix specifications: Suffix Specifications. - (line 6) -* transient state: Transient State. (line 6) -* transient-level: Enabling and Disabling Suffixes. - (line 15) -* value history: Using History. (line 6) - - -File: transient.info, Node: GNU General Public License, Prev: Concept Index, Up: Top - -Appendix F GNU General Public License -************************************* - - Version 3, 29 June 2007 - - Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/> - - Everyone is permitted to copy and distribute verbatim copies of this - license document, but changing it is not allowed. - -Preamble -======== - -The GNU General Public License is a free, copyleft license for software -and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program—to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers’ and authors’ protection, the GPL clearly explains -that there is no warranty for this free software. For both users’ and -authors’ sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users’ freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - -TERMS AND CONDITIONS -==================== - - 0. Definitions. - - “This License” refers to version 3 of the GNU General Public - License. - - “Copyright” also means copyright-like laws that apply to other - kinds of works, such as semiconductor masks. - - “The Program” refers to any copyrightable work licensed under this - License. Each licensee is addressed as “you”. “Licensees” and - “recipients” may be individuals or organizations. - - To “modify” a work means to copy from or adapt all or part of the - work in a fashion requiring copyright permission, other than the - making of an exact copy. The resulting work is called a “modified - version” of the earlier work or a work “based on” the earlier work. - - A “covered work” means either the unmodified Program or a work - based on the Program. - - To “propagate” a work means to do anything with it that, without - permission, would make you directly or secondarily liable for - infringement under applicable copyright law, except executing it on - a computer or modifying a private copy. Propagation includes - copying, distribution (with or without modification), making - available to the public, and in some countries other activities as - well. - - To “convey” a work means any kind of propagation that enables other - parties to make or receive copies. Mere interaction with a user - through a computer network, with no transfer of a copy, is not - conveying. - - An interactive user interface displays “Appropriate Legal Notices” - to the extent that it includes a convenient and prominently visible - feature that (1) displays an appropriate copyright notice, and (2) - tells the user that there is no warranty for the work (except to - the extent that warranties are provided), that licensees may convey - the work under this License, and how to view a copy of this - License. If the interface presents a list of user commands or - options, such as a menu, a prominent item in the list meets this - criterion. - - 1. Source Code. - - The “source code” for a work means the preferred form of the work - for making modifications to it. “Object code” means any non-source - form of a work. - - A “Standard Interface” means an interface that either is an - official standard defined by a recognized standards body, or, in - the case of interfaces specified for a particular programming - language, one that is widely used among developers working in that - language. - - The “System Libraries” of an executable work include anything, - other than the work as a whole, that (a) is included in the normal - form of packaging a Major Component, but which is not part of that - Major Component, and (b) serves only to enable use of the work with - that Major Component, or to implement a Standard Interface for - which an implementation is available to the public in source code - form. A “Major Component”, in this context, means a major - essential component (kernel, window system, and so on) of the - specific operating system (if any) on which the executable work - runs, or a compiler used to produce the work, or an object code - interpreter used to run it. - - The “Corresponding Source” for a work in object code form means all - the source code needed to generate, install, and (for an executable - work) run the object code and to modify the work, including scripts - to control those activities. However, it does not include the - work’s System Libraries, or general-purpose tools or generally - available free programs which are used unmodified in performing - those activities but which are not part of the work. For example, - Corresponding Source includes interface definition files associated - with source files for the work, and the source code for shared - libraries and dynamically linked subprograms that the work is - specifically designed to require, such as by intimate data - communication or control flow between those subprograms and other - parts of the work. - - The Corresponding Source need not include anything that users can - regenerate automatically from other parts of the Corresponding - Source. - - The Corresponding Source for a work in source code form is that - same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of - copyright on the Program, and are irrevocable provided the stated - conditions are met. This License explicitly affirms your unlimited - permission to run the unmodified Program. The output from running - a covered work is covered by this License only if the output, given - its content, constitutes a covered work. This License acknowledges - your rights of fair use or other equivalent, as provided by - copyright law. - - You may make, run and propagate covered works that you do not - convey, without conditions so long as your license otherwise - remains in force. You may convey covered works to others for the - sole purpose of having them make modifications exclusively for you, - or provide you with facilities for running those works, provided - that you comply with the terms of this License in conveying all - material for which you do not control copyright. Those thus making - or running the covered works for you must do so exclusively on your - behalf, under your direction and control, on terms that prohibit - them from making any copies of your copyrighted material outside - their relationship with you. - - Conveying under any other circumstances is permitted solely under - the conditions stated below. Sublicensing is not allowed; section - 10 makes it unnecessary. - - 3. Protecting Users’ Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological - measure under any applicable law fulfilling obligations under - article 11 of the WIPO copyright treaty adopted on 20 December - 1996, or similar laws prohibiting or restricting circumvention of - such measures. - - When you convey a covered work, you waive any legal power to forbid - circumvention of technological measures to the extent such - circumvention is effected by exercising rights under this License - with respect to the covered work, and you disclaim any intention to - limit operation or modification of the work as a means of - enforcing, against the work’s users, your or third parties’ legal - rights to forbid circumvention of technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program’s source code as you - receive it, in any medium, provided that you conspicuously and - appropriately publish on each copy an appropriate copyright notice; - keep intact all notices stating that this License and any - non-permissive terms added in accord with section 7 apply to the - code; keep intact all notices of the absence of any warranty; and - give all recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, - and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to - produce it from the Program, in the form of source code under the - terms of section 4, provided that you also meet all of these - conditions: - - a. The work must carry prominent notices stating that you - modified it, and giving a relevant date. - - b. The work must carry prominent notices stating that it is - released under this License and any conditions added under - section 7. This requirement modifies the requirement in - section 4 to “keep intact all notices”. - - c. You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable - section 7 additional terms, to the whole of the work, and all - its parts, regardless of how they are packaged. This License - gives no permission to license the work in any other way, but - it does not invalidate such permission if you have separately - received it. - - d. If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has - interactive interfaces that do not display Appropriate Legal - Notices, your work need not make them do so. - - A compilation of a covered work with other separate and independent - works, which are not by their nature extensions of the covered - work, and which are not combined with it such as to form a larger - program, in or on a volume of a storage or distribution medium, is - called an “aggregate” if the compilation and its resulting - copyright are not used to limit the access or legal rights of the - compilation’s users beyond what the individual works permit. - Inclusion of a covered work in an aggregate does not cause this - License to apply to the other parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms - of sections 4 and 5, provided that you also convey the - machine-readable Corresponding Source under the terms of this - License, in one of these ways: - - a. Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b. Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that - product model, to give anyone who possesses the object code - either (1) a copy of the Corresponding Source for all the - software in the product that is covered by this License, on a - durable physical medium customarily used for software - interchange, for a price no more than your reasonable cost of - physically performing this conveying of source, or (2) access - to copy the Corresponding Source from a network server at no - charge. - - c. Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, - and only if you received the object code with such an offer, - in accord with subsection 6b. - - d. Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to - the Corresponding Source in the same way through the same - place at no further charge. You need not require recipients - to copy the Corresponding Source along with the object code. - If the place to copy the object code is a network server, the - Corresponding Source may be on a different server (operated by - you or a third party) that supports equivalent copying - facilities, provided you maintain clear directions next to the - object code saying where to find the Corresponding Source. - Regardless of what server hosts the Corresponding Source, you - remain obligated to ensure that it is available for as long as - needed to satisfy these requirements. - - e. Convey the object code using peer-to-peer transmission, - provided you inform other peers where the object code and - Corresponding Source of the work are being offered to the - general public at no charge under subsection 6d. - - A separable portion of the object code, whose source code is - excluded from the Corresponding Source as a System Library, need - not be included in conveying the object code work. - - A “User Product” is either (1) a “consumer product”, which means - any tangible personal property which is normally used for personal, - family, or household purposes, or (2) anything designed or sold for - incorporation into a dwelling. In determining whether a product is - a consumer product, doubtful cases shall be resolved in favor of - coverage. For a particular product received by a particular user, - “normally used” refers to a typical or common use of that class of - product, regardless of the status of the particular user or of the - way in which the particular user actually uses, or expects or is - expected to use, the product. A product is a consumer product - regardless of whether the product has substantial commercial, - industrial or non-consumer uses, unless such uses represent the - only significant mode of use of the product. - - “Installation Information” for a User Product means any methods, - procedures, authorization keys, or other information required to - install and execute modified versions of a covered work in that - User Product from a modified version of its Corresponding Source. - The information must suffice to ensure that the continued - functioning of the modified object code is in no case prevented or - interfered with solely because modification has been made. - - If you convey an object code work under this section in, or with, - or specifically for use in, a User Product, and the conveying - occurs as part of a transaction in which the right of possession - and use of the User Product is transferred to the recipient in - perpetuity or for a fixed term (regardless of how the transaction - is characterized), the Corresponding Source conveyed under this - section must be accompanied by the Installation Information. But - this requirement does not apply if neither you nor any third party - retains the ability to install modified object code on the User - Product (for example, the work has been installed in ROM). - - The requirement to provide Installation Information does not - include a requirement to continue to provide support service, - warranty, or updates for a work that has been modified or installed - by the recipient, or for the User Product in which it has been - modified or installed. Access to a network may be denied when the - modification itself materially and adversely affects the operation - of the network or violates the rules and protocols for - communication across the network. - - Corresponding Source conveyed, and Installation Information - provided, in accord with this section must be in a format that is - publicly documented (and with an implementation available to the - public in source code form), and must require no special password - or key for unpacking, reading or copying. - - 7. Additional Terms. - - “Additional permissions” are terms that supplement the terms of - this License by making exceptions from one or more of its - conditions. Additional permissions that are applicable to the - entire Program shall be treated as though they were included in - this License, to the extent that they are valid under applicable - law. If additional permissions apply only to part of the Program, - that part may be used separately under those permissions, but the - entire Program remains governed by this License without regard to - the additional permissions. - - When you convey a copy of a covered work, you may at your option - remove any additional permissions from that copy, or from any part - of it. (Additional permissions may be written to require their own - removal in certain cases when you modify the work.) You may place - additional permissions on material, added by you to a covered work, - for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material - you add to a covered work, you may (if authorized by the copyright - holders of that material) supplement the terms of this License with - terms: - - a. Disclaiming warranty or limiting liability differently from - the terms of sections 15 and 16 of this License; or - - b. Requiring preservation of specified reasonable legal notices - or author attributions in that material or in the Appropriate - Legal Notices displayed by works containing it; or - - c. Prohibiting misrepresentation of the origin of that material, - or requiring that modified versions of such material be marked - in reasonable ways as different from the original version; or - - d. Limiting the use for publicity purposes of names of licensors - or authors of the material; or - - e. Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f. Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified - versions of it) with contractual assumptions of liability to - the recipient, for any liability that these contractual - assumptions directly impose on those licensors and authors. - - All other non-permissive additional terms are considered “further - restrictions” within the meaning of section 10. If the Program as - you received it, or any part of it, contains a notice stating that - it is governed by this License along with a term that is a further - restriction, you may remove that term. If a license document - contains a further restriction but permits relicensing or conveying - under this License, you may add to a covered work material governed - by the terms of that license document, provided that the further - restriction does not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you - must place, in the relevant source files, a statement of the - additional terms that apply to those files, or a notice indicating - where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in - the form of a separately written license, or stated as exceptions; - the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly - provided under this License. Any attempt otherwise to propagate or - modify it is void, and will automatically terminate your rights - under this License (including any patent licenses granted under the - third paragraph of section 11). - - However, if you cease all violation of this License, then your - license from a particular copyright holder is reinstated (a) - provisionally, unless and until the copyright holder explicitly and - finally terminates your license, and (b) permanently, if the - copyright holder fails to notify you of the violation by some - reasonable means prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is - reinstated permanently if the copyright holder notifies you of the - violation by some reasonable means, this is the first time you have - received notice of violation of this License (for any work) from - that copyright holder, and you cure the violation prior to 30 days - after your receipt of the notice. - - Termination of your rights under this section does not terminate - the licenses of parties who have received copies or rights from you - under this License. If your rights have been terminated and not - permanently reinstated, you do not qualify to receive new licenses - for the same material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or - run a copy of the Program. Ancillary propagation of a covered work - occurring solely as a consequence of using peer-to-peer - transmission to receive a copy likewise does not require - acceptance. However, nothing other than this License grants you - permission to propagate or modify any covered work. These actions - infringe copyright if you do not accept this License. Therefore, - by modifying or propagating a covered work, you indicate your - acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically - receives a license from the original licensors, to run, modify and - propagate that work, subject to this License. You are not - responsible for enforcing compliance by third parties with this - License. - - An “entity transaction” is a transaction transferring control of an - organization, or substantially all assets of one, or subdividing an - organization, or merging organizations. If propagation of a - covered work results from an entity transaction, each party to that - transaction who receives a copy of the work also receives whatever - licenses to the work the party’s predecessor in interest had or - could give under the previous paragraph, plus a right to possession - of the Corresponding Source of the work from the predecessor in - interest, if the predecessor has it or can get it with reasonable - efforts. - - You may not impose any further restrictions on the exercise of the - rights granted or affirmed under this License. For example, you - may not impose a license fee, royalty, or other charge for exercise - of rights granted under this License, and you may not initiate - litigation (including a cross-claim or counterclaim in a lawsuit) - alleging that any patent claim is infringed by making, using, - selling, offering for sale, or importing the Program or any portion - of it. - - 11. Patents. - - A “contributor” is a copyright holder who authorizes use under this - License of the Program or a work on which the Program is based. - The work thus licensed is called the contributor’s “contributor - version”. - - A contributor’s “essential patent claims” are all patent claims - owned or controlled by the contributor, whether already acquired or - hereafter acquired, that would be infringed by some manner, - permitted by this License, of making, using, or selling its - contributor version, but do not include claims that would be - infringed only as a consequence of further modification of the - contributor version. For purposes of this definition, “control” - includes the right to grant patent sublicenses in a manner - consistent with the requirements of this License. - - Each contributor grants you a non-exclusive, worldwide, - royalty-free patent license under the contributor’s essential - patent claims, to make, use, sell, offer for sale, import and - otherwise run, modify and propagate the contents of its contributor - version. - - In the following three paragraphs, a “patent license” is any - express agreement or commitment, however denominated, not to - enforce a patent (such as an express permission to practice a - patent or covenant not to sue for patent infringement). To “grant” - such a patent license to a party means to make such an agreement or - commitment not to enforce a patent against the party. - - If you convey a covered work, knowingly relying on a patent - license, and the Corresponding Source of the work is not available - for anyone to copy, free of charge and under the terms of this - License, through a publicly available network server or other - readily accessible means, then you must either (1) cause the - Corresponding Source to be so available, or (2) arrange to deprive - yourself of the benefit of the patent license for this particular - work, or (3) arrange, in a manner consistent with the requirements - of this License, to extend the patent license to downstream - recipients. “Knowingly relying” means you have actual knowledge - that, but for the patent license, your conveying the covered work - in a country, or your recipient’s use of the covered work in a - country, would infringe one or more identifiable patents in that - country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or - arrangement, you convey, or propagate by procuring conveyance of, a - covered work, and grant a patent license to some of the parties - receiving the covered work authorizing them to use, propagate, - modify or convey a specific copy of the covered work, then the - patent license you grant is automatically extended to all - recipients of the covered work and works based on it. - - A patent license is “discriminatory” if it does not include within - the scope of its coverage, prohibits the exercise of, or is - conditioned on the non-exercise of one or more of the rights that - are specifically granted under this License. You may not convey a - covered work if you are a party to an arrangement with a third - party that is in the business of distributing software, under which - you make payment to the third party based on the extent of your - activity of conveying the work, and under which the third party - grants, to any of the parties who would receive the covered work - from you, a discriminatory patent license (a) in connection with - copies of the covered work conveyed by you (or copies made from - those copies), or (b) primarily for and in connection with specific - products or compilations that contain the covered work, unless you - entered into that arrangement, or that patent license was granted, - prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting - any implied license or other defenses to infringement that may - otherwise be available to you under applicable patent law. - - 12. No Surrender of Others’ Freedom. - - If conditions are imposed on you (whether by court order, agreement - or otherwise) that contradict the conditions of this License, they - do not excuse you from the conditions of this License. If you - cannot convey a covered work so as to satisfy simultaneously your - obligations under this License and any other pertinent obligations, - then as a consequence you may not convey it at all. For example, - if you agree to terms that obligate you to collect a royalty for - further conveying from those to whom you convey the Program, the - only way you could satisfy both those terms and this License would - be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have - permission to link or combine any covered work with a work licensed - under version 3 of the GNU Affero General Public License into a - single combined work, and to convey the resulting work. The terms - of this License will continue to apply to the part which is the - covered work, but the special requirements of the GNU Affero - General Public License, section 13, concerning interaction through - a network will apply to the combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new - versions of the GNU General Public License from time to time. Such - new versions will be similar in spirit to the present version, but - may differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the - Program specifies that a certain numbered version of the GNU - General Public License “or any later version” applies to it, you - have the option of following the terms and conditions either of - that numbered version or of any later version published by the Free - Software Foundation. If the Program does not specify a version - number of the GNU General Public License, you may choose any - version ever published by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future - versions of the GNU General Public License can be used, that - proxy’s public statement of acceptance of a version permanently - authorizes you to choose that version for the Program. - - Later license versions may give you additional or different - permissions. However, no additional obligations are imposed on any - author or copyright holder as a result of your choosing to follow a - later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY - APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE - COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” - WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, - INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE - RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. - SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL - NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN - WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES - AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR - DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR - CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE - THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA - BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD - PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER - PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF - THE POSSIBILITY OF SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided - above cannot be given local legal effect according to their terms, - reviewing courts shall apply local law that most closely - approximates an absolute waiver of all civil liability in - connection with the Program, unless a warranty or assumption of - liability accompanies a copy of the Program in return for a fee. - -END OF TERMS AND CONDITIONS -=========================== - -How to Apply These Terms to Your New Programs -============================================= - -If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these -terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least the -“copyright” line and a pointer to where the full notice is found. - - ONE LINE TO GIVE THE PROGRAM'S NAME AND A BRIEF IDEA OF WHAT IT DOES. - Copyright (C) YEAR NAME OF AUTHOR - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or (at - your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. - - Also add information on how to contact you by electronic and paper -mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - PROGRAM Copyright (C) YEAR NAME OF AUTHOR - This program comes with ABSOLUTELY NO WARRANTY; for details type ‘show w’. - This is free software, and you are welcome to redistribute it - under certain conditions; type ‘show c’ for details. - - The hypothetical commands ‘show w’ and ‘show c’ should show the -appropriate parts of the General Public License. Of course, your -program’s commands might be different; for a GUI interface, you would -use an “about box”. - - You should also get your employer (if you work as a programmer) or -school, if any, to sign a “copyright disclaimer” for the program, if -necessary. For more information on this, and how to apply and follow -the GNU GPL, see <https://www.gnu.org/licenses/>. - - The GNU General Public License does not permit incorporating your -program into proprietary programs. If your program is a subroutine -library, you may consider it more useful to permit linking proprietary -applications with the library. If this is what you want to do, use the -GNU Lesser General Public License instead of this License. But first, -please read <https://www.gnu.org/licenses/why-not-lgpl.html>. - - - -Tag Table: -Node: Top763 -Node: Introduction2976 -Ref: Some things that Transient can do3504 -Ref: Complexity in CLI programs3857 -Ref: Using Transient for composing interactive commands4458 -Node: Usage6700 -Node: Invoking Transients7068 -Node: Aborting and Resuming Transients8147 -Node: Common Suffix Commands10768 -Node: Saving Values12604 -Ref: Saving Values-Footnote-113975 -Node: Using History14168 -Node: Getting Help for Suffix Commands15742 -Node: Enabling and Disabling Suffixes17120 -Node: Other Commands20408 -Node: Configuration21384 -Ref: Essential Options21664 -Ref: Accessibility Options25325 -Ref: Auxiliary Options25648 -Ref: Developer Options30611 -Node: Modifying Existing Transients31859 -Node: Defining New Commands36051 -Node: Technical Introduction36414 -Node: Defining Transients42115 -Node: Binding Suffix and Infix Commands44582 -Node: Group Specifications45440 -Node: Suffix Specifications50541 -Node: Defining Suffix and Infix Commands54754 -Node: Using Infix Arguments57802 -Node: Transient State60636 -Ref: Pre-commands for Infixes65451 -Ref: Pre-commands for Suffixes65971 -Ref: Pre-commands for Non-Suffixes68425 -Ref: Special Pre-Commands69561 -Node: Classes and Methods70069 -Node: Group Classes72253 -Node: Group Methods74180 -Node: Prefix Classes75433 -Node: Suffix Classes76524 -Node: Suffix Methods79611 -Node: Suffix Value Methods79932 -Node: Suffix Format Methods82690 -Node: Prefix Slots84169 -Ref: Internal Prefix Slots86304 -Node: Suffix Slots87561 -Ref: Slots of transient-suffix87929 -Ref: Slots of transient-infix89066 -Ref: Slots of transient-variable92362 -Ref: Slots of transient-switches92464 -Node: Predicate Slots92827 -Node: FAQ94262 -Ref: Can I control how the popup buffer is displayed?94391 -Ref: How can I copy text from the popup buffer?94572 -Ref: How does Transient compare to prefix keys and universal arguments?95066 -Ref: How does Transient compare to Magit-Popup and Hydra?95309 -Ref: Why did some of the key bindings change?95503 -Ref: Why does q not quit popups anymore?97856 -Node: Keystroke Index98959 -Node: Command and Function Index100824 -Node: Variable Index107416 -Node: Concept Index109689 -Node: GNU General Public License112425 - -End Tag Table - - -Local Variables: -coding: utf-8 -End: diff --git a/emacs/elpa/transient-20240525.1118/dir b/emacs/elpa/transient-20240607.1832/dir diff --git a/emacs/elpa/transient-20240525.1118/gpl.info b/emacs/elpa/transient-20240607.1832/gpl.info diff --git a/emacs/elpa/transient-20240525.1118/transient-autoloads.el b/emacs/elpa/transient-20240607.1832/transient-autoloads.el diff --git a/emacs/elpa/transient-20240607.1832/transient-pkg.el b/emacs/elpa/transient-20240607.1832/transient-pkg.el @@ -0,0 +1,16 @@ +(define-package "transient" "20240607.1832" "Transient commands" + '((emacs "26.1") + (compat "29.1.4.4") + (seq "2.24")) + :commit "872b19b062653797e997db4907da59315ed16c5b" :authors + '(("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev")) + :maintainers + '(("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev")) + :maintainer + '("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev") + :keywords + '("extensions") + :url "https://github.com/magit/transient") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs/elpa/transient-20240607.1832/transient.el b/emacs/elpa/transient-20240607.1832/transient.el @@ -0,0 +1,4508 @@ +;;; transient.el --- Transient commands -*- lexical-binding:t -*- + +;; Copyright (C) 2018-2024 Free Software Foundation, Inc. + +;; Author: Jonas Bernoulli <emacs.transient@jonas.bernoulli.dev> +;; Homepage: https://github.com/magit/transient +;; Keywords: extensions + +;; Package-Version: 0.6.0 +;; Package-Requires: ((emacs "26.1") (compat "29.1.4.4") (seq "2.24")) + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation, either version 3 of the License, +;; or (at your option) any later version. +;; +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Transient is the library used to implement the keyboard-driven menus +;; in Magit. It is distributed as a separate package, so that it can be +;; used to implement similar menus in other packages. + +;;; Code: + +(require 'cl-lib) +(require 'compat) +(require 'eieio) +(require 'edmacro) +(require 'format-spec) + +(eval-and-compile + (when (and (featurep' seq) + (not (fboundp 'seq-keep))) + (unload-feature 'seq 'force))) +(require 'seq) +(unless (fboundp 'seq-keep) + (display-warning 'transient (substitute-command-keys "\ +Transient requires `seq' >= 2.24, +but due to bad defaults, Emacs' package manager, refuses to +upgrade this and other built-in packages to higher releases +from GNU Elpa, when a package specifies that this is needed. + +To fix this, you have to add this to your init file: + + (setq package-install-upgrade-built-in t) + +Then evaluate that expression by placing the cursor after it +and typing \\[eval-last-sexp]. + +Once you have done that, you have to explicitly upgrade `seq': + + \\[package-upgrade] seq \\`RET' + +Then you also must make sure the updated version is loaded, +by evaluating this form: + + (progn (unload-feature 'seq t) (require 'seq)) + +Until you do this, you will get random errors about `seq-keep' +being undefined while using Transient. + +If you don't use the `package' package manager but still get +this warning, then your chosen package manager likely has a +similar defect.") :emergency)) + +(eval-when-compile (require 'subr-x)) + +(declare-function info "info" (&optional file-or-node buffer)) +(declare-function Man-find-section "man" (section)) +(declare-function Man-next-section "man" (n)) +(declare-function Man-getpage-in-background "man" (topic)) + +(defvar Man-notify-method) +(defvar pp-default-function) ; since Emacs 29.1 + +(defmacro static-if (condition then-form &rest else-forms) + "A conditional compilation macro. +Evaluate CONDITION at macro-expansion time. If it is non-nil, +expand the macro to THEN-FORM. Otherwise expand it to ELSE-FORMS +enclosed in a `progn' form. ELSE-FORMS may be empty." + (declare (indent 2) + (debug (sexp sexp &rest sexp))) + (if (eval condition lexical-binding) + then-form + (cons 'progn else-forms))) + +(defmacro transient--with-emergency-exit (id &rest body) + (declare (indent defun)) + (unless (keywordp id) + (setq body (cons id body)) + (setq id nil)) + `(condition-case err + (let ((debugger #'transient--exit-and-debug)) + ,(macroexp-progn body)) + ((debug error) + (transient--emergency-exit ,id) + (signal (car err) (cdr err))))) + +(defun transient--exit-and-debug (&rest args) + (transient--emergency-exit :debugger) + (apply #'debug args)) + +;;; Options + +(defgroup transient nil + "Transient commands." + :group 'extensions) + +(defcustom transient-show-popup t + "Whether to show the current transient in a popup buffer. +\\<transient-map> +- If t, then show the popup as soon as a transient prefix command + is invoked. + +- If nil, then do not show the popup unless the user explicitly + requests it, by pressing \\[transient-show] or a prefix key. + +- If a number, then delay displaying the popup and instead show + a brief one-line summary. If zero or negative, then suppress + even showing that summary and display the pressed key only. + + Show the popup when the user explicitly requests it by pressing + \\[transient-show] or a prefix key. Unless zero, then also show the popup + after that many seconds of inactivity (using the absolute value)." + :package-version '(transient . "0.1.0") + :group 'transient + :type '(choice (const :tag "instantly" t) + (const :tag "on demand" nil) + (const :tag "on demand (no summary)" 0) + (number :tag "after delay" 1))) + +(defcustom transient-enable-popup-navigation t + "Whether navigation commands are enabled in the transient popup. + +While a transient is active the transient popup buffer is not the +current buffer, making it necessary to use dedicated commands to +act on that buffer itself. If this is non-nil, then the following +bindings are available: + +\\<transient-popup-navigation-map>\ +- \\[transient-backward-button] moves the cursor to the previous suffix. +- \\[transient-forward-button] moves the cursor to the next suffix. +- \\[transient-push-button] invokes the suffix the cursor is on. +\\<transient-button-map>\ +- \\`<mouse-1>' and \\`<mouse-2>' invoke the clicked on suffix. +\\<transient-popup-navigation-map>\ +- \\[transient-isearch-backward]\ + and \\[transient-isearch-forward] start isearch in the popup buffer. + +\\`<mouse-1>' and \\`<mouse-2>' are bound in `transient-push-button'. +All other bindings are in `transient-popup-navigation-map'. + +By default \\`M-RET' is bound to `transient-push-button', instead of +\\`RET', because if a transient allows the invocation of non-suffixes +then it is likely that you would want \\`RET' to do what it would do +if no transient were active." + :package-version '(transient . "0.4.0") + :group 'transient + :type 'boolean) + +(defcustom transient-display-buffer-action + '(display-buffer-in-side-window + (side . bottom) + (dedicated . t) + (inhibit-same-window . t) + (window-parameters (no-other-window . t))) + "The action used to display the transient popup buffer. + +The transient popup buffer is displayed in a window using + + (display-buffer BUFFER transient-display-buffer-action) + +The value of this option has the form (FUNCTION . ALIST), +where FUNCTION is a function or a list of functions. Each such +function should accept two arguments: a buffer to display and an +alist of the same form as ALIST. See info node `(elisp)Choosing +Window' for details. + +The default is: + + (display-buffer-in-side-window + (side . bottom) + (dedicated . t) + (inhibit-same-window . t) + (window-parameters (no-other-window . t))) + +This displays the window at the bottom of the selected frame. +Another useful FUNCTION is `display-buffer-below-selected', which +is what `magit-popup' used by default. For more alternatives see +info node `(elisp)Display Action Functions' and info node +`(elisp)Buffer Display Action Alists'. + +Note that the buffer that was current before the transient buffer +is shown should remain the current buffer. Many suffix commands +act on the thing at point, if appropriate, and if the transient +buffer became the current buffer, then that would change what is +at point. To that effect `inhibit-same-window' ensures that the +selected window is not used to show the transient buffer. + +It may be possible to display the window in another frame, but +whether that works in practice depends on the window-manager. +If the window manager selects the new window (Emacs frame), +then that unfortunately changes which buffer is current. + +If you change the value of this option, then you might also +want to change the value of `transient-mode-line-format'." + :package-version '(transient . "0.3.0") + :group 'transient + :type '(cons (choice function (repeat :tag "Functions" function)) + alist)) + +(defcustom transient-mode-line-format 'line + "The mode-line format for the transient popup buffer. + +If nil, then the buffer has no mode-line. If the buffer is not +displayed right above the echo area, then this probably is not +a good value. + +If `line' (the default) or a natural number, then the buffer +has no mode-line, but a line is drawn is drawn in its place. +If a number is used, that specifies the thickness of the line. +On termcap frames we cannot draw lines, so there `line' and +numbers are synonyms for nil. + +The color of the line is used to indicate if non-suffixes are +allowed and whether they exit the transient. The foreground +color of `transient-key-noop' (if non-suffix are disallowed), +`transient-key-stay' (if allowed and transient stays active), or +`transient-key-exit' (if allowed and they exit the transient) is +used to draw the line. + +Otherwise this can be any mode-line format. +See `mode-line-format' for details." + :package-version '(transient . "0.2.0") + :group 'transient + :type '(choice (const :tag "hide mode-line" nil) + (const :tag "substitute thin line" line) + (number :tag "substitute line with thickness") + (const :tag "name of prefix command" + ("%e" mode-line-front-space + mode-line-buffer-identification)) + (sexp :tag "custom mode-line format"))) + +(defcustom transient-show-common-commands nil + "Whether to show common transient suffixes in the popup buffer. + +These commands are always shown after typing the prefix key +\"C-x\" when a transient command is active. To toggle the value +of this variable use \"C-x t\" when a transient is active." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'boolean) + +(defcustom transient-read-with-initial-input nil + "Whether to use the last history element as initial minibuffer input." + :package-version '(transient . "0.2.0") + :group 'transient + :type 'boolean) + +(defcustom transient-highlight-mismatched-keys nil + "Whether to highlight keys that do not match their argument. + +This only affects infix arguments that represent command-line +arguments. When this option is non-nil, then the key binding +for infix argument are highlighted when only a long argument +\(e.g., \"--verbose\") is specified but no shorthand (e.g., \"-v\"). +In the rare case that a short-hand is specified but does not +match the key binding, then it is highlighted differently. + +The highlighting is done using `transient-mismatched-key' +and `transient-nonstandard-key'." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'boolean) + +(defcustom transient-highlight-higher-levels nil + "Whether to highlight suffixes on higher levels. + +This is primarily intended for package authors. + +When non-nil then highlight the description of suffixes whose +level is above 4, the default of `transient-default-level'. +Assuming you have set that variable to 7, this highlights all +suffixes that won't be available to users without them making +the same customization." + :package-version '(transient . "0.3.6") + :group 'transient + :type 'boolean) + +(defcustom transient-substitute-key-function nil + "Function used to modify key bindings. + +This function is called with one argument, the prefix object, +and must return a key binding description, either the existing +key description it finds in the `key' slot, or a substitution. + +This is intended to let users replace certain prefix keys. It +could also be used to make other substitutions, but that is +discouraged. + +For example, \"=\" is hard to reach using my custom keyboard +layout, so I substitute \"(\" for that, which is easy to reach +using a layout optimized for Lisp. + + (setq transient-substitute-key-function + (lambda (obj) + (let ((key (oref obj key))) + (if (string-match \"\\\\`\\\\(=\\\\)[a-zA-Z]\" key) + (replace-match \"(\" t t key 1) + key)))))" + :package-version '(transient . "0.1.0") + :group 'transient + :type '(choice (const :tag "Transform no keys (nil)" nil) function)) + +(defcustom transient-semantic-coloring t + "Whether to use colors to indicate transient behavior. + +If non-nil, then the key binding of each suffix is colorized to +indicate whether it exits the transient state or not, and the +line that is drawn below the transient popup buffer is used to +indicate the behavior of non-suffix commands." + :package-version '(transient . "0.5.0") + :group 'transient + :type 'boolean) + +(defcustom transient-detect-key-conflicts nil + "Whether to detect key binding conflicts. + +Conflicts are detected when a transient prefix command is invoked +and results in an error, which prevents the transient from being +used." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'boolean) + +(defcustom transient-align-variable-pitch nil + "Whether to align columns pixel-wise in the popup buffer. + +If this is non-nil, then columns are aligned pixel-wise to +support variable-pitch fonts. Keys are not aligned, so you +should use a fixed-pitch font for the `transient-key' face. +Other key faces inherit from that face unless a theme is +used that breaks that relationship. + +This option is intended for users who use a variable-pitch +font for the `default' face. + +Also see `transient-force-fixed-pitch'." + :package-version '(transient . "0.4.0") + :group 'transient + :type 'boolean) + +(defcustom transient-force-fixed-pitch nil + "Whether to force use of monospaced font in the popup buffer. + +Even if you use a proportional font for the `default' face, +you might still want to use a monospaced font in transient's +popup buffer. Setting this option to t causes `default' to +be remapped to `fixed-pitch' in that buffer. + +Also see `transient-align-variable-pitch'." + :package-version '(transient . "0.2.0") + :group 'transient + :type 'boolean) + +(defcustom transient-force-single-column nil + "Whether to force use of a single column to display suffixes. + +This might be useful for users with low vision who use large +text and might otherwise have to scroll in two dimensions." + :package-version '(transient . "0.3.6") + :group 'transient + :type 'boolean) + +(defcustom transient-hide-during-minibuffer-read nil + "Whether to hide the transient buffer while reading in the minibuffer." + :package-version '(transient . "0.4.0") + :group 'transient + :type 'boolean) + +(defconst transient--max-level 7) +(defconst transient--default-child-level 1) +(defconst transient--default-prefix-level 4) + +(defcustom transient-default-level transient--default-prefix-level + "Control what suffix levels are made available by default. + +Each suffix command is placed on a level and each prefix command +has a level, which controls which suffix commands are available. +Integers between 1 and 7 (inclusive) are valid levels. + +The levels of individual transients and/or their individual +suffixes can be changed individually, by invoking the prefix and +then pressing \"C-x l\". + +The default level for both transients and their suffixes is 4. +This option only controls the default for transients. The default +suffix level is always 4. The author of a transient should place +certain suffixes on a higher level if they expect that it won't be +of use to most users, and they should place very important suffixes +on a lower level so that they remain available even if the user +lowers the transient level. + +\(Magit currently places nearly all suffixes on level 4 and lower +levels are not used at all yet. So for the time being you should +not set a lower level here and using a higher level might not +give you as many additional suffixes as you hoped.)" + :package-version '(transient . "0.1.0") + :group 'transient + :type '(choice (const :tag "1 - fewest suffixes" 1) + (const 2) + (const 3) + (const :tag "4 - default" 4) + (const 5) + (const 6) + (const :tag "7 - most suffixes" 7))) + +(defcustom transient-levels-file + (locate-user-emacs-file "transient/levels.el") + "File used to save levels of transients and their suffixes." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'file) + +(defcustom transient-values-file + (locate-user-emacs-file "transient/values.el") + "File used to save values of transients." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'file) + +(defcustom transient-history-file + (locate-user-emacs-file "transient/history.el") + "File used to save history of transients and their infixes." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'file) + +(defcustom transient-history-limit 10 + "Number of history elements to keep when saving to file." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'integer) + +(defcustom transient-save-history t + "Whether to save history of transient commands when exiting Emacs." + :package-version '(transient . "0.1.0") + :group 'transient + :type 'boolean) + +;;; Faces + +(defgroup transient-faces nil + "Faces used by Transient." + :group 'transient) + +(defface transient-heading '((t :inherit font-lock-keyword-face)) + "Face used for headings." + :group 'transient-faces) + +(defface transient-argument '((t :inherit font-lock-string-face :weight bold)) + "Face used for enabled arguments." + :group 'transient-faces) + +(defface transient-inactive-argument '((t :inherit shadow)) + "Face used for inactive arguments." + :group 'transient-faces) + +(defface transient-value '((t :inherit font-lock-string-face :weight bold)) + "Face used for values." + :group 'transient-faces) + +(defface transient-inactive-value '((t :inherit shadow)) + "Face used for inactive values." + :group 'transient-faces) + +(defface transient-unreachable '((t :inherit shadow)) + "Face used for suffixes unreachable from the current prefix sequence." + :group 'transient-faces) + +(defface transient-inapt-suffix '((t :inherit shadow :italic t)) + "Face used for suffixes that are inapt at this time." + :group 'transient-faces) + +(defface transient-active-infix '((t :inherit highlight)) + "Face used for the infix for which the value is being read." + :group 'transient-faces) + +(defface transient-enabled-suffix + '((t :background "green" :foreground "black" :weight bold)) + "Face used for enabled levels while editing suffix levels. +See info node `(transient)Enabling and Disabling Suffixes'." + :group 'transient-faces) + +(defface transient-disabled-suffix + '((t :background "red" :foreground "black" :weight bold)) + "Face used for disabled levels while editing suffix levels. +See info node `(transient)Enabling and Disabling Suffixes'." + :group 'transient-faces) + +(defface transient-higher-level + `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) + :color ,(let ((color (face-attribute 'shadow :foreground nil t))) + (or (and (not (eq color 'unspecified)) color) + "grey60"))))) + "Face optionally used to highlight suffixes on higher levels. +Also see option `transient-highlight-higher-levels'." + :group 'transient-faces) + +(defface transient-delimiter '((t :inherit shadow)) + "Face used for delimiters and separators. +This includes the parentheses around values and the pipe +character used to separate possible values from each other." + :group 'transient-faces) + +(defface transient-key '((t :inherit font-lock-builtin-face)) + "Face used for keys." + :group 'transient-faces) + +(defface transient-key-stay + `((((class color) (background light)) + :inherit transient-key + :foreground "#22aa22") + (((class color) (background dark)) + :inherit transient-key + :foreground "#ddffdd")) + "Face used for keys of suffixes that don't exit transient state." + :group 'transient-faces) + +(defface transient-key-noop + `((((class color) (background light)) + :inherit transient-key + :foreground "grey80") + (((class color) (background dark)) + :inherit transient-key + :foreground "grey30")) + "Face used for keys of suffixes that currently cannot be invoked." + :group 'transient-faces) + +(defface transient-key-return + `((((class color) (background light)) + :inherit transient-key + :foreground "#aaaa11") + (((class color) (background dark)) + :inherit transient-key + :foreground "#ffffcc")) + "Face used for keys of suffixes that return to the parent transient." + :group 'transient-faces) + +(defface transient-key-exit + `((((class color) (background light)) + :inherit transient-key + :foreground "#aa2222") + (((class color) (background dark)) + :inherit transient-key + :foreground "#ffdddd")) + "Face used for keys of suffixes that exit transient state." + :group 'transient-faces) + +(defface transient-unreachable-key + '((t :inherit (shadow transient-key) :weight normal)) + "Face used for keys unreachable from the current prefix sequence." + :group 'transient-faces) + +(defface transient-nonstandard-key + `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) + :color "cyan"))) + "Face optionally used to highlight keys conflicting with short-argument. +Also see option `transient-highlight-mismatched-keys'." + :group 'transient-faces) + +(defface transient-mismatched-key + `((t :box ( :line-width ,(if (>= emacs-major-version 28) (cons -1 -1) -1) + :color "magenta"))) + "Face optionally used to highlight keys without a short-argument. +Also see option `transient-highlight-mismatched-keys'." + :group 'transient-faces) + +;;; Persistence + +(defun transient--read-file-contents (file) + (with-demoted-errors "Transient error: %S" + (and (file-exists-p file) + (with-temp-buffer + (insert-file-contents file) + (read (current-buffer)))))) + +(defun transient--pp-to-file (list file) + (make-directory (file-name-directory file) t) + (setq list (cl-sort (copy-sequence list) #'string< :key #'car)) + (with-temp-file file + (let ((print-level nil) + (print-length nil) + (pp-default-function 'pp-28) + (fill-column 999)) + (pp list (current-buffer))))) + +(defvar transient-values + (transient--read-file-contents transient-values-file) + "Values of transient commands. +The value of this variable persists between Emacs sessions +and you usually should not change it manually.") + +(defun transient-save-values () + (transient--pp-to-file transient-values transient-values-file)) + +(defvar transient-levels + (transient--read-file-contents transient-levels-file) + "Levels of transient commands. +The value of this variable persists between Emacs sessions +and you usually should not change it manually.") + +(defun transient-save-levels () + (transient--pp-to-file transient-levels transient-levels-file)) + +(defvar transient-history + (transient--read-file-contents transient-history-file) + "History of transient commands and infix arguments. +The value of this variable persists between Emacs sessions +\(unless `transient-save-history' is nil) and you usually +should not change it manually.") + +(defun transient-save-history () + (setq transient-history + (cl-sort (mapcar (pcase-lambda (`(,key . ,val)) + (cons key (seq-take (delete-dups val) + transient-history-limit))) + transient-history) + #'string< :key #'car)) + (transient--pp-to-file transient-history transient-history-file)) + +(defun transient-maybe-save-history () + "Save the value of `transient-history'. +If `transient-save-history' is nil, then do nothing." + (when transient-save-history + (transient-save-history))) + +(unless noninteractive + (add-hook 'kill-emacs-hook #'transient-maybe-save-history)) + +;;; Classes +;;;; Prefix + +(defclass transient-prefix () + ((prototype :initarg :prototype) + (command :initarg :command) + (level :initarg :level) + (variable :initarg :variable :initform nil) + (init-value :initarg :init-value) + (value) (default-value :initarg :value) + (scope :initarg :scope :initform nil) + (history :initarg :history :initform nil) + (history-pos :initarg :history-pos :initform 0) + (history-key :initarg :history-key :initform nil) + (show-help :initarg :show-help :initform nil) + (info-manual :initarg :info-manual :initform nil) + (man-page :initarg :man-page :initform nil) + (transient-suffix :initarg :transient-suffix :initform nil) + (transient-non-suffix :initarg :transient-non-suffix :initform nil) + (transient-switch-frame :initarg :transient-switch-frame) + (refresh-suffixes :initarg :refresh-suffixes :initform nil) + (incompatible :initarg :incompatible :initform nil) + (suffix-description :initarg :suffix-description) + (variable-pitch :initarg :variable-pitch :initform nil) + (column-widths :initarg :column-widths :initform nil) + (unwind-suffix :documentation "Internal use." :initform nil)) + "Transient prefix command. + +Each transient prefix command consists of a command, which is +stored in a symbol's function slot and an object, which is +stored in the `transient--prefix' property of the same symbol. + +When a transient prefix command is invoked, then a clone of that +object is stored in the global variable `transient--prefix' and +the prototype is stored in the clone's `prototype' slot.") + +;;;; Suffix + +(defclass transient-child () + ((level + :initarg :level + :initform (symbol-value 'transient--default-child-level) + :documentation "Enable if level of prefix is equal or greater.") + (if + :initarg :if + :initform nil + :documentation "Enable if predicate returns non-nil.") + (if-not + :initarg :if-not + :initform nil + :documentation "Enable if predicate returns nil.") + (if-non-nil + :initarg :if-non-nil + :initform nil + :documentation "Enable if variable's value is non-nil.") + (if-nil + :initarg :if-nil + :initform nil + :documentation "Enable if variable's value is nil.") + (if-mode + :initarg :if-mode + :initform nil + :documentation "Enable if major-mode matches value.") + (if-not-mode + :initarg :if-not-mode + :initform nil + :documentation "Enable if major-mode does not match value.") + (if-derived + :initarg :if-derived + :initform nil + :documentation "Enable if major-mode derives from value.") + (if-not-derived + :initarg :if-not-derived + :initform nil + :documentation "Enable if major-mode does not derive from value.") + (inapt + :initform nil) + (inapt-face + :initarg :inapt-face + :initform 'transient-inapt-suffix) + (inapt-if + :initarg :inapt-if + :initform nil + :documentation "Inapt if predicate returns non-nil.") + (inapt-if-not + :initarg :inapt-if-not + :initform nil + :documentation "Inapt if predicate returns nil.") + (inapt-if-non-nil + :initarg :inapt-if-non-nil + :initform nil + :documentation "Inapt if variable's value is non-nil.") + (inapt-if-nil + :initarg :inapt-if-nil + :initform nil + :documentation "Inapt if variable's value is nil.") + (inapt-if-mode + :initarg :inapt-if-mode + :initform nil + :documentation "Inapt if major-mode matches value.") + (inapt-if-not-mode + :initarg :inapt-if-not-mode + :initform nil + :documentation "Inapt if major-mode does not match value.") + (inapt-if-derived + :initarg :inapt-if-derived + :initform nil + :documentation "Inapt if major-mode derives from value.") + (inapt-if-not-derived + :initarg :inapt-if-not-derived + :initform nil + :documentation "Inapt if major-mode does not derive from value.")) + "Abstract superclass for group and suffix classes. + +It is undefined what happens if more than one `if*' predicate +slot is non-nil." + :abstract t) + +(defclass transient-suffix (transient-child) + ((definition :allocation :class :initform nil) + (key :initarg :key) + (command :initarg :command) + (transient :initarg :transient) + (format :initarg :format :initform " %k %d") + (description :initarg :description :initform nil) + (face :initarg :face :initform nil) + (show-help :initarg :show-help :initform nil)) + "Superclass for suffix command.") + +(defclass transient-information (transient-suffix) + ((format :initform " %k %d") + (key :initform " ")) + "Display-only information, aligned with suffix keys. +Technically a suffix object with no associated command.") + +(defclass transient-information* (transient-information) + ((format :initform " %d")) + "Display-only information, aligned with suffix descriptions. +Technically a suffix object with no associated command.") + +(defclass transient-infix (transient-suffix) + ((transient :initform t) + (argument :initarg :argument) + (shortarg :initarg :shortarg) + (value :initform nil) + (init-value :initarg :init-value) + (unsavable :initarg :unsavable :initform nil) + (multi-value :initarg :multi-value :initform nil) + (always-read :initarg :always-read :initform nil) + (allow-empty :initarg :allow-empty :initform nil) + (history-key :initarg :history-key :initform nil) + (reader :initarg :reader :initform nil) + (prompt :initarg :prompt :initform nil) + (choices :initarg :choices :initform nil) + (format :initform " %k %d (%v)")) + "Transient infix command." + :abstract t) + +(defclass transient-argument (transient-infix) () + "Abstract superclass for infix arguments." + :abstract t) + +(defclass transient-switch (transient-argument) () + "Class used for command-line argument that can be turned on and off.") + +(defclass transient-option (transient-argument) () + "Class used for command-line argument that can take a value.") + +(defclass transient-variable (transient-infix) + ((variable :initarg :variable) + (format :initform " %k %d %v")) + "Abstract superclass for infix commands that set a variable." + :abstract t) + +(defclass transient-switches (transient-argument) + ((argument-format :initarg :argument-format) + (argument-regexp :initarg :argument-regexp)) + "Class used for sets of mutually exclusive command-line switches.") + +(defclass transient-files (transient-option) () + ((key :initform "--") + (argument :initform "--") + (multi-value :initform rest) + (reader :initform transient-read-files)) + "Class used for the \"--\" argument or similar. +All remaining arguments are treated as files. +They become the value of this argument.") + +;;;; Group + +(defclass transient-group (transient-child) + ((suffixes :initarg :suffixes :initform nil) + (hide :initarg :hide :initform nil) + (description :initarg :description :initform nil) + (pad-keys :initarg :pad-keys :initform nil) + (info-format :initarg :info-format :initform nil) + (setup-children :initarg :setup-children)) + "Abstract superclass of all group classes." + :abstract t) + +(defclass transient-column (transient-group) () + "Group class that displays each element on a separate line.") + +(defclass transient-row (transient-group) () + "Group class that displays all elements on a single line.") + +(defclass transient-columns (transient-group) () + "Group class that displays elements organized in columns. +Direct elements have to be groups whose elements have to be +commands or strings. Each subgroup represents a column. +This class takes care of inserting the subgroups' elements.") + +(defclass transient-subgroups (transient-group) () + "Group class that wraps other groups. + +Direct elements have to be groups whose elements have to be +commands or strings. This group inserts an empty line between +subgroups. The subgroups are responsible for displaying their +elements themselves.") + +;;; Define + +(defmacro transient-define-prefix (name arglist &rest args) + "Define NAME as a transient prefix command. + +ARGLIST are the arguments that command takes. +DOCSTRING is the documentation string and is optional. + +These arguments can optionally be followed by key-value pairs. +Each key has to be a keyword symbol, either `:class' or a keyword +argument supported by the constructor of that class. The +`transient-prefix' class is used if the class is not specified +explicitly. + +GROUPs add key bindings for infix and suffix commands and specify +how these bindings are presented in the popup buffer. At least +one GROUP has to be specified. See info node `(transient)Binding +Suffix and Infix Commands'. + +The BODY is optional. If it is omitted, then ARGLIST is also +ignored and the function definition becomes: + + (lambda () + (interactive) + (transient-setup \\='NAME)) + +If BODY is specified, then it must begin with an `interactive' +form that matches ARGLIST, and it must call `transient-setup'. +It may however call that function only when some condition is +satisfied; that is one of the reason why you might want to use +an explicit BODY. + +All transients have a (possibly nil) value, which is exported +when suffix commands are called, so that they can consume that +value. For some transients it might be necessary to have a sort +of secondary value, called a scope. Such a scope would usually +be set in the commands `interactive' form and has to be passed +to the setup function: + + (transient-setup \\='NAME nil nil :scope SCOPE) + +\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]... GROUP... [BODY...])" + (declare (debug ( &define name lambda-list + [&optional lambda-doc] + [&rest keywordp sexp] + [&rest vectorp] + [&optional ("interactive" interactive) def-body])) + (indent defun) + (doc-string 3)) + (pcase-let ((`(,class ,slots ,suffixes ,docstr ,body ,interactive-only) + (transient--expand-define-args args arglist))) + `(progn + (defalias ',name + ,(if body + `(lambda ,arglist ,@body) + `(lambda () + (interactive) + (transient-setup ',name)))) + (put ',name 'interactive-only ,interactive-only) + (put ',name 'function-documentation ,docstr) + (put ',name 'transient--prefix + (,(or class 'transient-prefix) :command ',name ,@slots)) + (put ',name 'transient--layout + (list ,@(cl-mapcan (lambda (s) (transient--parse-child name s)) + suffixes)))))) + +(defmacro transient-define-suffix (name arglist &rest args) + "Define NAME as a transient suffix command. + +ARGLIST are the arguments that the command takes. +DOCSTRING is the documentation string and is optional. + +These arguments can optionally be followed by key-value pairs. +Each key has to be a keyword symbol, either `:class' or a +keyword argument supported by the constructor of that class. +The `transient-suffix' class is used if the class is not +specified explicitly. + +The BODY must begin with an `interactive' form that matches +ARGLIST. The infix arguments are usually accessed by using +`transient-args' inside `interactive'. + +\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]... [BODY...])" + (declare (debug ( &define name lambda-list + [&optional lambda-doc] + [&rest keywordp sexp] + [&optional ("interactive" interactive) def-body])) + (indent defun) + (doc-string 3)) + (pcase-let ((`(,class ,slots ,_ ,docstr ,body ,interactive-only) + (transient--expand-define-args args arglist))) + `(progn + (defalias ',name + ,(if (and (not body) class (oref-default class definition)) + `(oref-default ',class definition) + `(lambda ,arglist ,@body))) + (put ',name 'interactive-only ,interactive-only) + (put ',name 'function-documentation ,docstr) + (put ',name 'transient--suffix + (,(or class 'transient-suffix) :command ',name ,@slots))))) + +(defmacro transient-define-infix (name arglist &rest args) + "Define NAME as a transient infix command. + +ARGLIST is always ignored and reserved for future use. +DOCSTRING is the documentation string and is optional. + +At least one key-value pair is required. All transient infix +commands are equal to each other (but not eq). It is meaning- +less to define an infix command, without providing at least one +keyword argument (usually `:argument' or `:variable', depending +on the class). The suffix class defaults to `transient-switch' +and can be set using the `:class' keyword. + +The function definitions is always: + + (lambda () + (interactive) + (let ((obj (transient-suffix-object))) + (transient-infix-set obj (transient-infix-read obj))) + (transient--show)) + +`transient-infix-read' and `transient-infix-set' are generic +functions. Different infix commands behave differently because +the concrete methods are different for different infix command +classes. In rare case the above command function might not be +suitable, even if you define your own infix command class. In +that case you have to use `transient-define-suffix' to define +the infix command and use t as the value of the `:transient' +keyword. + +\(fn NAME ARGLIST [DOCSTRING] KEYWORD VALUE [KEYWORD VALUE]...)" + (declare (debug ( &define name lambda-list + [&optional lambda-doc] + keywordp sexp + [&rest keywordp sexp])) + (indent defun) + (doc-string 3)) + (pcase-let ((`(,class ,slots ,_ ,docstr ,_ ,interactive-only) + (transient--expand-define-args args arglist t))) + `(progn + (defalias ',name #'transient--default-infix-command) + (put ',name 'interactive-only ,interactive-only) + (put ',name 'completion-predicate #'transient--suffix-only) + (put ',name 'function-documentation ,docstr) + (put ',name 'transient--suffix + (,(or class 'transient-switch) :command ',name ,@slots))))) + +(defalias 'transient-define-argument #'transient-define-infix + "Define NAME as a transient infix command. + +Only use this alias to define an infix command that actually +sets an infix argument. To define a infix command that, for +example, sets a variable, use `transient-define-infix' instead. + +\(fn NAME ARGLIST [DOCSTRING] [KEYWORD VALUE]...)") + +(defun transient--default-infix-command () + ;; Most infix commands are but an alias for this command. + "Cannot show any documentation for this transient infix command. + +When you request help for an infix command using `transient-help', that +usually shows the respective man-page and tries to jump to the location +where the respective argument is being described. + +If no man-page is specified for the containing transient menu, then the +docstring is displayed instead, if any. + +If the infix command doesn't have a docstring, as is the case here, then +this docstring is displayed instead, because technically infix commands +are aliases for `transient--default-infix-command'. + +`describe-function' also shows the docstring of the infix command, +falling back to that of the same aliased command." + (interactive) + (let ((obj (transient-suffix-object))) + (transient-infix-set obj (transient-infix-read obj))) + (transient--show)) +(put 'transient--default-infix-command 'interactive-only t) +(put 'transient--default-infix-command 'completion-predicate + #'transient--suffix-only) + +(defun transient--find-function-advised-original (fn func) + "Return nil instead of `transient--default-infix-command'. +When using `find-function' to jump to the definition of a transient +infix command/argument, then we want to actually jump to that, not to +the definition of `transient--default-infix-command', which all infix +commands are aliases for." + (let ((val (funcall fn func))) + (and val (not (eq val 'transient--default-infix-command)) val))) +(advice-add 'find-function-advised-original :around + #'transient--find-function-advised-original) + +(eval-and-compile + (defun transient--expand-define-args (args &optional arglist nobody) + (unless (listp arglist) + (error "Mandatory ARGLIST is missing")) + (let (class keys suffixes docstr declare (interactive-only t)) + (when (stringp (car args)) + (setq docstr (pop args))) + (while (keywordp (car args)) + (let ((k (pop args)) + (v (pop args))) + (if (eq k :class) + (setq class v) + (push k keys) + (push v keys)))) + (while (let ((arg (car args))) + (or (vectorp arg) + (and arg (symbolp arg)))) + (push (pop args) suffixes)) + (when (eq (car-safe (car args)) 'declare) + (setq declare (car args)) + (setq args (cdr args)) + (when-let ((int (assq 'interactive-only declare))) + (setq interactive-only (cadr int)) + (delq int declare)) + (unless (cdr declare) + (setq declare nil))) + (cond + ((not args)) + (nobody + (error "transient-define-infix: No function body allowed")) + ((not (eq (car-safe (nth (if declare 1 0) args)) 'interactive)) + (error "transient-define-*: Interactive form missing"))) + (list (if (eq (car-safe class) 'quote) + (cadr class) + class) + (nreverse keys) + (nreverse suffixes) + docstr + (if declare (cons declare args) args) + interactive-only)))) + +(defun transient--parse-child (prefix spec) + (cl-typecase spec + (null (error "Invalid transient--parse-child spec: %s" spec)) + (symbol (let ((value (symbol-value spec))) + (if (and (listp value) + (or (listp (car value)) + (vectorp (car value)))) + (cl-mapcan (lambda (s) (transient--parse-child prefix s)) value) + (transient--parse-child prefix value)))) + (vector (and-let* ((c (transient--parse-group prefix spec))) (list c))) + (list (and-let* ((c (transient--parse-suffix prefix spec))) (list c))) + (string (list spec)) + (t (error "Invalid transient--parse-child spec: %s" spec)))) + +(defun transient--parse-group (prefix spec) + (setq spec (append spec nil)) + (cl-symbol-macrolet + ((car (car spec)) + (pop (pop spec))) + (let (level class args) + (when (integerp car) + (setq level pop)) + (when (stringp car) + (setq args (plist-put args :description pop))) + (while (keywordp car) + (let ((key pop) + (val pop)) + (cond ((eq key :class) + (setq class (macroexp-quote val))) + ((or (symbolp val) + (and (listp val) (not (eq (car val) 'lambda)))) + (setq args (plist-put args key (macroexp-quote val)))) + ((setq args (plist-put args key val)))))) + (unless (or spec class (not (plist-get args :setup-children))) + (message "WARNING: %s: When %s is used, %s must also be specified" + 'transient-define-prefix :setup-children :class)) + (list 'vector + (or level transient--default-child-level) + (cond (class) + ((or (vectorp car) + (and car (symbolp car))) + (quote 'transient-columns)) + ((quote 'transient-column))) + (and args (cons 'list args)) + (cons 'list + (cl-mapcan (lambda (s) (transient--parse-child prefix s)) + spec)))))) + +(defun transient--parse-suffix (prefix spec) + (let (level class args) + (cl-symbol-macrolet + ((car (car spec)) + (pop (pop spec))) + (when (integerp car) + (setq level pop)) + (when (or (stringp car) + (vectorp car)) + (setq args (plist-put args :key pop))) + (cond + ((or (stringp car) + (and (eq (car-safe car) 'lambda) + (not (commandp car)))) + (setq args (plist-put args :description pop))) + ((and (symbolp car) + (not (keywordp car)) + (not (commandp car)) + (commandp (cadr spec))) + (setq args (plist-put args :description (macroexp-quote pop))))) + (cond + ((memq car '(:info :info*))) + ((keywordp car) + (error "Need command, `:info' or `:info*', got `%s'" car)) + ((symbolp car) + (setq args (plist-put args :command (macroexp-quote pop)))) + ((and (commandp car) + (not (stringp car))) + (let ((cmd pop) + (sym (intern + (format "transient:%s:%s" + prefix + (let ((desc (plist-get args :description))) + (if (and (stringp desc) + (length< desc 16)) + desc + (plist-get args :key))))))) + (setq args (plist-put + args :command + `(prog1 ',sym + (put ',sym 'interactive-only t) + (put ',sym 'completion-predicate #'transient--suffix-only) + (defalias ',sym + ,(if (eq (car-safe cmd) 'lambda) + cmd + (macroexp-quote cmd)))))))) + ((or (stringp car) + (and car (listp car))) + (let ((arg pop) + (sym nil)) + (cl-typecase arg + (list + (setq args (plist-put args :shortarg (car arg))) + (setq args (plist-put args :argument (cadr arg))) + (setq arg (cadr arg))) + (string + (when-let ((shortarg (transient--derive-shortarg arg))) + (setq args (plist-put args :shortarg shortarg))) + (setq args (plist-put args :argument arg)))) + (setq sym (intern (format "transient:%s:%s" prefix arg))) + (setq args (plist-put + args :command + `(prog1 ',sym + (put ',sym 'interactive-only t) + (put ',sym 'completion-predicate #'transient--suffix-only) + (defalias ',sym #'transient--default-infix-command)))) + (cond ((and car (not (keywordp car))) + (setq class 'transient-option) + (setq args (plist-put args :reader (macroexp-quote pop)))) + ((not (string-suffix-p "=" arg)) + (setq class 'transient-switch)) + (t + (setq class 'transient-option))))) + (t + (error "Needed command or argument, got %S" car))) + (while (keywordp car) + (let ((key pop) + (val pop)) + (cond ((eq key :class) (setq class val)) + ((eq key :level) (setq level val)) + ((eq key :info) + (setq class 'transient-information) + (setq args (plist-put args :description val))) + ((eq key :info*) + (setq class 'transient-information*) + (setq args (plist-put args :description val))) + ((eq (car-safe val) '\,) + (setq args (plist-put args key (cadr val)))) + ((or (symbolp val) + (and (listp val) (not (eq (car val) 'lambda)))) + (setq args (plist-put args key (macroexp-quote val)))) + ((setq args (plist-put args key val))))))) + (unless (plist-get args :key) + (when-let ((shortarg (plist-get args :shortarg))) + (setq args (plist-put args :key shortarg)))) + (list 'list + (or level transient--default-child-level) + (macroexp-quote (or class 'transient-suffix)) + (cons 'list args)))) + +(defun transient--derive-shortarg (arg) + (save-match-data + (and (string-match "\\`\\(-[a-zA-Z]\\)\\(\\'\\|=\\)" arg) + (match-string 1 arg)))) + +(defun transient-command-completion-not-suffix-only-p (symbol _buffer) + "Say whether SYMBOL should be offered as a completion. +If the value of SYMBOL's `completion-predicate' property is +`transient--suffix-only', then return nil, otherwise return t. +This is the case when a command should only ever be used as a +suffix of a transient prefix command (as opposed to bindings +in regular keymaps or by using `execute-extended-command')." + (not (eq (get symbol 'completion-predicate) 'transient--suffix-only))) + +(defalias 'transient--suffix-only #'ignore + "Ignore ARGUMENTS, do nothing, and return nil. +Also see `transient-command-completion-not-suffix-only-p'. +Only use this alias as the value of the `completion-predicate' +symbol property.") + +(when (and (boundp 'read-extended-command-predicate) ; since Emacs 28.1 + (not read-extended-command-predicate)) + (setq read-extended-command-predicate + #'transient-command-completion-not-suffix-only-p)) + +(defun transient-parse-suffix (prefix suffix) + "Parse SUFFIX, to be added to PREFIX. +PREFIX is a prefix command, a symbol. +SUFFIX is a suffix command or a group specification (of + the same forms as expected by `transient-define-prefix'). +Intended for use in a group's `:setup-children' function." + (cl-assert (and prefix (symbolp prefix))) + (eval (car (transient--parse-child prefix suffix)) t)) + +(defun transient-parse-suffixes (prefix suffixes) + "Parse SUFFIXES, to be added to PREFIX. +PREFIX is a prefix command, a symbol. +SUFFIXES is a list of suffix command or a group specification + (of the same forms as expected by `transient-define-prefix'). +Intended for use in a group's `:setup-children' function." + (cl-assert (and prefix (symbolp prefix))) + (mapcar (apply-partially #'transient-parse-suffix prefix) suffixes)) + +;;; Edit + +(defun transient--insert-suffix (prefix loc suffix action &optional keep-other) + (let* ((suf (cl-etypecase suffix + (vector (transient--parse-group prefix suffix)) + (list (transient--parse-suffix prefix suffix)) + (string suffix))) + (mem (transient--layout-member loc prefix)) + (elt (car mem))) + (setq suf (eval suf t)) + (cond + ((not mem) + (message "Cannot insert %S into %s; %s not found" + suffix prefix loc)) + ((or (and (vectorp suffix) (not (vectorp elt))) + (and (listp suffix) (vectorp elt)) + (and (stringp suffix) (vectorp elt))) + (message "Cannot place %S into %s at %s; %s" + suffix prefix loc + "suffixes and groups cannot be siblings")) + (t + (when-let* ((bindingp (listp suf)) + (key (transient--spec-key suf)) + (conflict (car (transient--layout-member key prefix))) + (conflictp + (and (not (and (eq action 'replace) + (eq conflict elt))) + (or (not keep-other) + (eq (plist-get (nth 2 suf) :command) + (plist-get (nth 2 conflict) :command))) + (equal (transient--suffix-predicate suf) + (transient--suffix-predicate conflict))))) + (transient-remove-suffix prefix key)) + (pcase-exhaustive action + ('insert (setcdr mem (cons elt (cdr mem))) + (setcar mem suf)) + ('append (setcdr mem (cons suf (cdr mem)))) + ('replace (setcar mem suf))))))) + +;;;###autoload +(defun transient-insert-suffix (prefix loc suffix &optional keep-other) + "Insert a SUFFIX into PREFIX before LOC. +PREFIX is a prefix command, a symbol. +SUFFIX is a suffix command or a group specification (of + the same forms as expected by `transient-define-prefix'). +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +Remove a conflicting binding unless optional KEEP-OTHER is + non-nil. +See info node `(transient)Modifying Existing Transients'." + (declare (indent defun)) + (transient--insert-suffix prefix loc suffix 'insert keep-other)) + +;;;###autoload +(defun transient-append-suffix (prefix loc suffix &optional keep-other) + "Insert a SUFFIX into PREFIX after LOC. +PREFIX is a prefix command, a symbol. +SUFFIX is a suffix command or a group specification (of + the same forms as expected by `transient-define-prefix'). +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +Remove a conflicting binding unless optional KEEP-OTHER is + non-nil. +See info node `(transient)Modifying Existing Transients'." + (declare (indent defun)) + (transient--insert-suffix prefix loc suffix 'append keep-other)) + +;;;###autoload +(defun transient-replace-suffix (prefix loc suffix) + "Replace the suffix at LOC in PREFIX with SUFFIX. +PREFIX is a prefix command, a symbol. +SUFFIX is a suffix command or a group specification (of + the same forms as expected by `transient-define-prefix'). +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +See info node `(transient)Modifying Existing Transients'." + (declare (indent defun)) + (transient--insert-suffix prefix loc suffix 'replace)) + +;;;###autoload +(defun transient-remove-suffix (prefix loc) + "Remove the suffix or group at LOC in PREFIX. +PREFIX is a prefix command, a symbol. +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +See info node `(transient)Modifying Existing Transients'." + (declare (indent defun)) + (transient--layout-member loc prefix 'remove)) + +(defun transient-get-suffix (prefix loc) + "Return the suffix or group at LOC in PREFIX. +PREFIX is a prefix command, a symbol. +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +See info node `(transient)Modifying Existing Transients'." + (if-let ((mem (transient--layout-member loc prefix))) + (car mem) + (error "%s not found in %s" loc prefix))) + +(defun transient-suffix-put (prefix loc prop value) + "Edit the suffix at LOC in PREFIX, setting PROP to VALUE. +PREFIX is a prefix command, a symbol. +SUFFIX is a suffix command or a group specification (of + the same forms as expected by `transient-define-prefix'). +LOC is a command, a key vector, a key description (a string + as returned by `key-description'), or a coordination list + (whose last element may also be a command or key). +See info node `(transient)Modifying Existing Transients'." + (let ((suf (transient-get-suffix prefix loc))) + (setf (elt suf 2) + (plist-put (elt suf 2) prop value)))) + +(defun transient--layout-member (loc prefix &optional remove) + (let ((val (or (get prefix 'transient--layout) + (error "%s is not a transient command" prefix)))) + (when (listp loc) + (while (integerp (car loc)) + (let* ((children (if (vectorp val) (aref val 3) val)) + (mem (transient--nthcdr (pop loc) children))) + (if (and remove (not loc)) + (let ((rest (delq (car mem) children))) + (if (vectorp val) + (aset val 3 rest) + (put prefix 'transient--layout rest)) + (setq val nil)) + (setq val (if loc (car mem) mem))))) + (setq loc (car loc))) + (if loc + (transient--layout-member-1 (transient--kbd loc) val remove) + val))) + +(defun transient--layout-member-1 (loc layout remove) + (cond ((listp layout) + (seq-some (lambda (elt) (transient--layout-member-1 loc elt remove)) + layout)) + ((vectorp (car (aref layout 3))) + (seq-some (lambda (elt) (transient--layout-member-1 loc elt remove)) + (aref layout 3))) + (remove + (aset layout 3 + (delq (car (transient--group-member loc layout)) + (aref layout 3))) + nil) + ((transient--group-member loc layout)))) + +(defun transient--group-member (loc group) + (cl-member-if (lambda (suffix) + (and (listp suffix) + (let* ((def (nth 2 suffix)) + (cmd (plist-get def :command))) + (if (symbolp loc) + (eq cmd loc) + (equal (transient--kbd + (or (plist-get def :key) + (transient--command-key cmd))) + loc))))) + (aref group 3))) + +(defun transient--kbd (keys) + (when (vectorp keys) + (setq keys (key-description keys))) + (when (stringp keys) + (setq keys (kbd keys))) + keys) + +(defun transient--spec-key (spec) + (let ((plist (nth 2 spec))) + (or (plist-get plist :key) + (transient--command-key + (plist-get plist :command))))) + +(defun transient--command-key (cmd) + (and-let* ((obj (transient--suffix-prototype cmd))) + (cond ((slot-boundp obj 'key) + (oref obj key)) + ((slot-exists-p obj 'shortarg) + (if (slot-boundp obj 'shortarg) + (oref obj shortarg) + (transient--derive-shortarg (oref obj argument))))))) + +(defun transient--nthcdr (n list) + (nthcdr (if (< n 0) (- (length list) (abs n)) n) list)) + +;;; Variables + +(defvar transient-current-prefix nil + "The transient from which this suffix command was invoked. +This is an object representing that transient, use +`transient-current-command' to get the respective command.") + +(defvar transient-current-command nil + "The transient from which this suffix command was invoked. +This is a symbol representing that transient, use +`transient-current-prefix' to get the respective object.") + +(defvar transient-current-suffixes nil + "The suffixes of the transient from which this suffix command was invoked. +This is a list of objects. Usually it is sufficient to instead +use the function `transient-args', which returns a list of +values. In complex cases it might be necessary to use this +variable instead.") + +(defvar transient-exit-hook nil + "Hook run after exiting a transient.") + +(defvar transient-setup-buffer-hook nil + "Hook run when setting up the transient buffer. +That buffer is current and empty when this hook runs.") + +(defvar transient--prefix nil) +(defvar transient--layout nil) +(defvar transient--suffixes nil) + +(defconst transient--stay t "Do not exit the transient.") +(defconst transient--exit nil "Do exit the transient.") + +(defvar transient--exitp nil "Whether to exit the transient.") +(defvar transient--showp nil "Whether to show the transient popup buffer.") +(defvar transient--helpp nil "Whether help-mode is active.") +(defvar transient--editp nil "Whether edit-mode is active.") + +(defvar transient--refreshp nil + "Whether to refresh the transient completely.") + +(defvar transient--all-levels-p nil + "Whether temporary display of suffixes on all levels is active.") + +(defvar transient--timer nil) + +(defvar transient--stack nil) + +(defvar transient--minibuffer-depth 0) + +(defvar transient--buffer-name " *transient*" + "Name of the transient buffer.") + +(defvar transient--buffer nil + "The transient menu buffer.") + +(defvar transient--window nil + "The window used to display the transient popup buffer.") + +(defvar transient--original-window nil + "The window that was selected before the transient was invoked. +Usually it remains selected while the transient is active.") + +(defvar transient--original-buffer nil + "The buffer that was current before the transient was invoked. +Usually it remains current while the transient is active.") + +(defvar transient--restore-winconf nil + "Window configuration to restore after exiting help.") + +(defvar transient--shadowed-buffer nil + "The buffer that is temporarily shadowed by the transient buffer. +This is bound while the suffix predicate is being evaluated and while +drawing in the transient buffer.") + +(defvar transient--pending-suffix nil + "The suffix that is currently being processed. +This is bound while the suffix predicate is being evaluated, +and while functions that return faces are being evaluated.") + +(defvar transient--pending-group nil + "The group that is currently being processed. +This is bound while the suffixes are drawn in the transient buffer.") + +(defvar transient--debug nil + "Whether to put debug information into *Messages*.") + +(defvar transient--history nil) + +(defvar transient--scroll-commands + '(transient-scroll-up + transient-scroll-down + mwheel-scroll + scroll-bar-toolkit-scroll)) + +;;; Identities + +(defun transient-prefix-object () + "Return the current prefix as an object. + +While a transient is being setup or refreshed (which involves +preparing its suffixes) the variable `transient--prefix' can be +used to access the prefix object. Thus this is what has to be +used in suffix methods such as `transient-format-description', +and in object-specific functions that are stored in suffix slots +such as `description'. + +When a suffix command is invoked (i.e., in its `interactive' form +and function body) then the variable `transient-current-prefix' +has to be used instead. + +Two distinct variables are needed, because any prefix may itself +be used as a suffix of another prefix, and such sub-prefixes have +to be able to tell themselves apart from the prefix they were +invoked from. + +Regular suffix commands, which are not prefixes, do not have to +concern themselves with this distinction, so they can use this +function instead. In the context of a plain suffix, it always +returns the value of the appropriate variable." + (or transient--prefix transient-current-prefix)) + +(defun transient-suffix-object (&optional command) + "Return the object associated with the current suffix command. + +Each suffix commands is associated with an object, which holds +additional information about the suffix, such as its value (in +the case of an infix command, which is a kind of suffix command). + +This function is intended to be called by infix commands, which +are usually aliases of `transient--default-infix-command', which +is defined like this: + + (defun transient--default-infix-command () + (interactive) + (let ((obj (transient-suffix-object))) + (transient-infix-set obj (transient-infix-read obj))) + (transient--show)) + +\(User input is read outside of `interactive' to prevent the +command from being added to `command-history'. See #23.) + +Such commands need to be able to access their associated object +to guide how `transient-infix-read' reads the new value and to +store the read value. Other suffix commands (including non-infix +commands) may also need the object to guide their behavior. + +This function attempts to return the object associated with the +current suffix command even if the suffix command was not invoked +from a transient. (For some suffix command that is a valid thing +to do, for others it is not.) In that case nil may be returned, +if the command was not defined using one of the macros intended +to define such commands. + +The optional argument COMMAND is intended for internal use. If +you are contemplating using it in your own code, then you should +probably use this instead: + + (get COMMAND \\='transient--suffix)" + (when command + (cl-check-type command command)) + (cond + (transient--pending-suffix) + ((or transient--prefix + transient-current-prefix) + (let ((suffixes + (cl-remove-if-not + (lambda (obj) + (eq (oref obj command) + (or command + (if (eq this-command 'transient-set-level) + ;; This is how it can look up for which + ;; command it is setting the level. + this-original-command + this-command)))) + (or transient--suffixes + transient-current-suffixes)))) + (or (and (cdr suffixes) + (cl-find-if + (lambda (obj) + (equal (listify-key-sequence (transient--kbd (oref obj key))) + (listify-key-sequence (this-command-keys)))) + suffixes)) + (car suffixes)))) + ((and-let* ((obj (transient--suffix-prototype (or command this-command))) + (obj (clone obj))) + (progn ; work around debbugs#31840 + (transient-init-scope obj) + (transient-init-value obj) + obj))))) + +(defun transient--suffix-prototype (command) + (or (get command 'transient--suffix) + (seq-some (lambda (cmd) (get cmd 'transient--suffix)) + (function-alias-p command)))) + +;;; Keymaps + +(defvar-keymap transient-base-map + :doc "Parent of other keymaps used by Transient. + +This is the parent keymap of all the keymaps that are used in +all transients: `transient-map' (which in turn is the parent +of the transient-specific keymaps), `transient-edit-map' and +`transient-sticky-map'. + +If you change a binding here, then you might also have to edit +`transient-sticky-map' and `transient-common-commands'. While +the latter isn't a proper transient prefix command, it can be +edited using the same functions as used for transients. + +If you add a new command here, then you must also add a binding +to `transient-predicate-map'." + "ESC ESC ESC" #'transient-quit-all + "C-g" #'transient-quit-one + "C-q" #'transient-quit-all + "C-z" #'transient-suspend + "C-v" #'transient-scroll-up + "C-M-v" #'transient-scroll-down + "<next>" #'transient-scroll-up + "<prior>" #'transient-scroll-down) + +(defvar-keymap transient-map + :doc "Top-level keymap used by all transients. + +If you add a new command here, then you must also add a binding +to `transient-predicate-map'. Also see `transient-base-map'." + :parent transient-base-map + "C-u" #'universal-argument + "C--" #'negative-argument + "C-t" #'transient-show + "?" #'transient-help + "C-h" #'transient-help + ;; Also bound to "C-x p" and "C-x n" in transient-common-commands. + "C-M-p" #'transient-history-prev + "C-M-n" #'transient-history-next) + +(defvar-keymap transient-edit-map + :doc "Keymap that is active while a transient in is in \"edit mode\"." + :parent transient-base-map + "?" #'transient-help + "C-h" #'transient-help + "C-x l" #'transient-set-level) + +(defvar-keymap transient-sticky-map + :doc "Keymap that is active while an incomplete key sequence is active." + :parent transient-base-map + "C-g" #'transient-quit-seq) + +(defvar transient--common-command-prefixes '(?\C-x)) + +(put 'transient-common-commands + 'transient--layout + (list + (eval + (car (transient--parse-child + 'transient-common-commands + (vector + :hide + (lambda () + (and (not (memq + (car (bound-and-true-p transient--redisplay-key)) + transient--common-command-prefixes)) + (not transient-show-common-commands))) + (vector + "Value commands" + (list "C-x s " "Set" #'transient-set) + (list "C-x C-s" "Save" #'transient-save) + (list "C-x C-k" "Reset" #'transient-reset) + (list "C-x p " "Previous value" #'transient-history-prev) + (list "C-x n " "Next value" #'transient-history-next)) + (vector + "Sticky commands" + ;; Like `transient-sticky-map' except that + ;; "C-g" has to be bound to a different command. + (list "C-g" "Quit prefix or transient" #'transient-quit-one) + (list "C-q" "Quit transient stack" #'transient-quit-all) + (list "C-z" "Suspend transient stack" #'transient-suspend)) + (vector + "Customize" + (list "C-x t" 'transient-toggle-common :description + (lambda () + (if transient-show-common-commands + "Hide common commands" + "Show common permanently"))) + (list "C-x l" "Show/hide suffixes" #'transient-set-level) + (list "C-x a" #'transient-toggle-level-limit))))) + t))) + +(defvar-keymap transient-popup-navigation-map + :doc "One of the keymaps used when popup navigation is enabled. +See `transient-enable-popup-navigation'." + "<down-mouse-1>" #'transient-noop + "<up>" #'transient-backward-button + "<down>" #'transient-forward-button + "C-r" #'transient-isearch-backward + "C-s" #'transient-isearch-forward + "M-RET" #'transient-push-button) + +(defvar-keymap transient-button-map + :doc "One of the keymaps used when popup navigation is enabled. +See `transient-enable-popup-navigation'." + "<mouse-1>" #'transient-push-button + "<mouse-2>" #'transient-push-button) + +(defvar-keymap transient-resume-mode-map + :doc "Keymap for `transient-resume-mode'. + +This keymap remaps every command that would usually just quit the +documentation buffer to `transient-resume', which additionally +resumes the suspended transient." + "<remap> <Man-quit>" #'transient-resume + "<remap> <Info-exit>" #'transient-resume + "<remap> <quit-window>" #'transient-resume) + +(defvar-keymap transient-predicate-map + :doc "Base keymap used to map common commands to their transient behavior. + +The \"transient behavior\" of a command controls, among other +things, whether invoking the command causes the transient to be +exited or not, and whether infix arguments are exported before +doing so. + +Each \"key\" is a command that is common to all transients and +that is bound in `transient-map', `transient-edit-map', +`transient-sticky-map' and/or `transient-common-command'. + +Each binding is a \"pre-command\", a function that controls the +transient behavior of the respective command. + +For transient commands that are bound in individual transients, +the transient behavior is specified using the `:transient' slot +of the corresponding object." + "<transient-suspend>" #'transient--do-suspend + "<transient-help>" #'transient--do-stay + "<transient-set-level>" #'transient--do-stay + "<transient-history-prev>" #'transient--do-stay + "<transient-history-next>" #'transient--do-stay + "<universal-argument>" #'transient--do-stay + "<universal-argument-more>" #'transient--do-stay + "<negative-argument>" #'transient--do-minus + "<digit-argument>" #'transient--do-stay + "<top-level>" #'transient--do-quit-all + "<transient-quit-all>" #'transient--do-quit-all + "<transient-quit-one>" #'transient--do-quit-one + "<transient-quit-seq>" #'transient--do-stay + "<transient-show>" #'transient--do-stay + "<transient-update>" #'transient--do-stay + "<transient-toggle-common>" #'transient--do-stay + "<transient-set>" #'transient--do-call + "<transient-set-and-exit>" #'transient--do-exit + "<transient-save>" #'transient--do-call + "<transient-save-and-exit>" #'transient--do-exit + "<transient-reset>" #'transient--do-call + "<describe-key-briefly>" #'transient--do-stay + "<describe-key>" #'transient--do-stay + "<transient-scroll-up>" #'transient--do-stay + "<transient-scroll-down>" #'transient--do-stay + "<mwheel-scroll>" #'transient--do-stay + "<scroll-bar-toolkit-scroll>" #'transient--do-stay + "<transient-noop>" #'transient--do-noop + "<transient-mouse-push-button>" #'transient--do-move + "<transient-push-button>" #'transient--do-push-button + "<transient-backward-button>" #'transient--do-move + "<transient-forward-button>" #'transient--do-move + "<transient-isearch-backward>" #'transient--do-move + "<transient-isearch-forward>" #'transient--do-move + ;; If a valid but incomplete prefix sequence is followed by + ;; an unbound key, then Emacs calls the `undefined' command + ;; but does not set `this-command', `this-original-command' + ;; or `real-this-command' accordingly. Instead they are nil. + "<nil>" #'transient--do-warn + ;; Bound to the `mouse-movement' event, this command is similar + ;; to `ignore'. + "<ignore-preserving-kill-region>" #'transient--do-noop) + +(defvar transient--transient-map nil) +(defvar transient--predicate-map nil) +(defvar transient--redisplay-map nil) +(defvar transient--redisplay-key nil) + +(defun transient--push-keymap (var) + (let ((map (symbol-value var))) + (transient--debug " push %s%s" var (if map "" " VOID")) + (when map + (with-demoted-errors "transient--push-keymap: %S" + (internal-push-keymap map 'overriding-terminal-local-map))))) + +(defun transient--pop-keymap (var) + (let ((map (symbol-value var))) + (when map + (transient--debug " pop %s" var) + (with-demoted-errors "transient--pop-keymap: %S" + (internal-pop-keymap map 'overriding-terminal-local-map))))) + +(defun transient--make-transient-map () + (let ((map (make-sparse-keymap))) + (set-keymap-parent map (if transient--editp + transient-edit-map + transient-map)) + (dolist (obj transient--suffixes) + (let ((key (oref obj key))) + (when (vectorp key) + (setq key (key-description key)) + (oset obj key key)) + (when transient-substitute-key-function + (setq key (save-match-data + (funcall transient-substitute-key-function obj))) + (oset obj key key)) + (let* ((kbd (kbd key)) + (cmd (oref obj command)) + (alt (transient--lookup-key map kbd))) + (cond ((not alt) + (define-key map kbd cmd)) + ((eq alt cmd)) + ((transient--inapt-suffix-p obj)) + ((and-let* ((obj (transient-suffix-object alt))) + (transient--inapt-suffix-p obj)) + (define-key map kbd cmd)) + (transient-detect-key-conflicts + (error "Cannot bind %S to %s and also %s" + (string-trim key) cmd alt)) + ((define-key map kbd cmd)))))) + (when-let ((b (keymap-lookup map "-"))) (keymap-set map "<kp-subtract>" b)) + (when-let ((b (keymap-lookup map "="))) (keymap-set map "<kp-equal>" b)) + (when-let ((b (keymap-lookup map "+"))) (keymap-set map "<kp-add>" b)) + (when transient-enable-popup-navigation + ;; `transient--make-redisplay-map' maps only over bindings that are + ;; directly in the base keymap, so that cannot be a composed keymap. + (set-keymap-parent + map (make-composed-keymap + (keymap-parent map) + transient-popup-navigation-map))) + map)) + +(defun transient--make-predicate-map () + (let* ((default (transient--resolve-pre-command + (oref transient--prefix transient-suffix))) + (return (and transient--stack (eq default t))) + (map (make-sparse-keymap))) + (set-keymap-parent map transient-predicate-map) + (when (or (and (slot-boundp transient--prefix 'transient-switch-frame) + (transient--resolve-pre-command + (not (oref transient--prefix transient-switch-frame)))) + (memq (transient--resolve-pre-command + (oref transient--prefix transient-non-suffix)) + '(nil transient--do-warn transient--do-noop))) + (define-key map [handle-switch-frame] #'transient--do-suspend)) + (dolist (obj transient--suffixes) + (let* ((cmd (oref obj command)) + (kind (cond ((get cmd 'transient--prefix) 'prefix) + ((cl-typep obj 'transient-infix) 'infix) + (t 'suffix)))) + (cond + ((oref obj inapt) + (define-key map (vector cmd) #'transient--do-warn-inapt)) + ((slot-boundp obj 'transient) + (define-key map (vector cmd) + (pcase (list kind + (transient--resolve-pre-command (oref obj transient)) + return) + (`(prefix t ,_) #'transient--do-recurse) + (`(prefix nil ,_) #'transient--do-stack) + (`(infix t ,_) #'transient--do-stay) + (`(suffix t ,_) #'transient--do-call) + ('(suffix nil t) #'transient--do-return) + (`(,_ nil ,_) #'transient--do-exit) + (`(,_ ,do ,_) do)))) + ((not (lookup-key transient-predicate-map (vector cmd))) + (define-key map (vector cmd) + (pcase (list kind default return) + (`(prefix ,(or 'transient--do-stay 'transient--do-call) ,_) + #'transient--do-recurse) + (`(prefix t ,_) #'transient--do-recurse) + (`(prefix ,_ ,_) #'transient--do-stack) + (`(infix ,_ ,_) #'transient--do-stay) + (`(suffix t ,_) #'transient--do-call) + ('(suffix nil t) #'transient--do-return) + (`(suffix nil ,_) #'transient--do-exit) + (`(suffix ,do ,_) do))))))) + map)) + +(defun transient--make-redisplay-map () + (setq transient--redisplay-key + (pcase this-command + ('transient-update + (setq transient--showp t) + (setq unread-command-events + (listify-key-sequence (this-single-command-raw-keys)))) + ('transient-quit-seq + (setq unread-command-events + (butlast (listify-key-sequence + (this-single-command-raw-keys)) + 2)) + (butlast transient--redisplay-key)) + (_ nil))) + (let ((topmap (make-sparse-keymap)) + (submap (make-sparse-keymap))) + (when transient--redisplay-key + (define-key topmap (vconcat transient--redisplay-key) submap) + (set-keymap-parent submap transient-sticky-map)) + (map-keymap-internal + (lambda (key def) + (when (and (not (eq key ?\e)) + (listp def) + (keymapp def)) + (define-key topmap (vconcat transient--redisplay-key (list key)) + #'transient-update))) + (if transient--redisplay-key + (let ((key (vconcat transient--redisplay-key))) + (or (lookup-key transient--transient-map key) + (and-let* ((regular (lookup-key local-function-key-map key))) + (lookup-key transient--transient-map (vconcat regular))))) + transient--transient-map)) + topmap)) + +;;; Setup + +(defun transient-setup (&optional name layout edit &rest params) + "Setup the transient specified by NAME. + +This function is called by transient prefix commands to setup the +transient. In that case NAME is mandatory, LAYOUT and EDIT must +be nil and PARAMS may be (but usually is not) used to set, e.g., +the \"scope\" of the transient (see `transient-define-prefix'). + +This function is also called internally in which case LAYOUT and +EDIT may be non-nil." + (transient--debug 'setup) + (transient--with-emergency-exit :setup + (cond + ((not name) + ;; Switching between regular and edit mode. + (transient--pop-keymap 'transient--transient-map) + (transient--pop-keymap 'transient--redisplay-map) + (setq name (oref transient--prefix command)) + (setq params (list :scope (oref transient--prefix scope)))) + (transient--prefix + ;; Invoked as a ":transient-non-suffix 'transient--do-{stay,call}" + ;; of an outer prefix. Unlike the usual `transient--do-stack', + ;; these predicates fail to clean up after the outer prefix. + (transient--pop-keymap 'transient--transient-map) + (transient--pop-keymap 'transient--redisplay-map)) + ((not (or layout ; resuming parent/suspended prefix + transient-current-command)) ; entering child prefix + (transient--stack-zap)) ; replace suspended prefix, if any + (edit + ;; Returning from help to edit. + (setq transient--editp t))) + (transient--init-objects name layout params) + (transient--init-keymaps) + (transient--history-init transient--prefix) + (setq transient--original-window (selected-window)) + (setq transient--original-buffer (current-buffer)) + (setq transient--minibuffer-depth (minibuffer-depth)) + (transient--redisplay) + (transient--init-transient) + (transient--suspend-which-key-mode))) + +(cl-defgeneric transient-setup-children (group children) + "Setup the CHILDREN of GROUP. +If the value of the `setup-children' slot is non-nil, then call +that function with CHILDREN as the only argument and return the +value. Otherwise return CHILDREN as is." + (if (slot-boundp group 'setup-children) + (funcall (oref group setup-children) children) + children)) + +(defun transient--init-keymaps () + (setq transient--predicate-map (transient--make-predicate-map)) + (setq transient--transient-map (transient--make-transient-map)) + (setq transient--redisplay-map (transient--make-redisplay-map))) + +(defun transient--init-objects (&optional name layout params) + (if name + (setq transient--prefix (transient--init-prefix name params)) + (setq name (oref transient--prefix command))) + (setq transient--refreshp (oref transient--prefix refresh-suffixes)) + (setq transient--layout (or layout (transient--init-suffixes name))) + (setq transient--suffixes (transient--flatten-suffixes transient--layout))) + +(defun transient--init-prefix (name &optional params) + (let ((obj (let ((proto (get name 'transient--prefix))) + (apply #'clone proto + :prototype proto + :level (or (alist-get t (alist-get name transient-levels)) + transient-default-level) + params)))) + (transient--setup-recursion obj) + (transient-init-value obj) + obj)) + +(defun transient--init-suffixes (name) + (let ((levels (alist-get name transient-levels))) + (cl-mapcan (lambda (c) (transient--init-child levels c nil)) + (append (get name 'transient--layout) + (and (not transient--editp) + (get 'transient-common-commands + 'transient--layout)))))) + +(defun transient--flatten-suffixes (layout) + (cl-labels ((s (def) + (cond + ((stringp def) nil) + ((cl-typep def 'transient-information) nil) + ((listp def) (cl-mapcan #'s def)) + ((cl-typep def 'transient-group) + (cl-mapcan #'s (oref def suffixes))) + ((cl-typep def 'transient-suffix) + (list def))))) + (cl-mapcan #'s layout))) + +(defun transient--init-child (levels spec parent) + (cl-etypecase spec + (vector (transient--init-group levels spec parent)) + (list (transient--init-suffix levels spec parent)) + (string (list spec)))) + +(defun transient--init-group (levels spec parent) + (pcase-let ((`(,level ,class ,args ,children) (append spec nil))) + (and-let* (((transient--use-level-p level)) + (obj (apply class :level level args)) + ((transient--use-suffix-p obj)) + ((prog1 t + (when (or (and parent (oref parent inapt)) + (transient--inapt-suffix-p obj)) + (oset obj inapt t)))) + (suffixes (cl-mapcan + (lambda (c) (transient--init-child levels c obj)) + (transient-setup-children obj children)))) + (progn ; work around debbugs#31840 + (oset obj suffixes suffixes) + (list obj))))) + +(defun transient--init-suffix (levels spec parent) + (pcase-let* ((`(,level ,class ,args) spec) + (cmd (plist-get args :command)) + (key (transient--kbd (plist-get args :key))) + (level (or (alist-get (cons cmd key) levels nil nil #'equal) + (alist-get cmd levels) + level))) + (let ((fn (and (symbolp cmd) + (symbol-function cmd)))) + (when (autoloadp fn) + (transient--debug " autoload %s" cmd) + (autoload-do-load fn))) + (when (transient--use-level-p level) + (let ((obj (if (child-of-class-p class 'transient-information) + (apply class :level level args) + (unless (and cmd (symbolp cmd)) + (error "BUG: Non-symbolic suffix command: %s" cmd)) + (if-let ((proto (and cmd (transient--suffix-prototype cmd)))) + (apply #'clone proto :level level args) + (apply class :command cmd :level level args))))) + (cond ((not cmd)) + ((commandp cmd)) + ((or (cl-typep obj 'transient-switch) + (cl-typep obj 'transient-option)) + ;; As a temporary special case, if the package was compiled + ;; with an older version of Transient, then we must define + ;; "anonymous" switch and option commands here. + (defalias cmd #'transient--default-infix-command)) + ((transient--use-suffix-p obj) + (error "Suffix command %s is not defined or autoloaded" cmd))) + (unless (cl-typep obj 'transient-information) + (transient--init-suffix-key obj)) + (when (transient--use-suffix-p obj) + (if (or (and parent (oref parent inapt)) + (transient--inapt-suffix-p obj)) + (oset obj inapt t) + (transient-init-scope obj) + (transient-init-value obj)) + (list obj)))))) + +(cl-defmethod transient--init-suffix-key ((obj transient-suffix)) + (unless (slot-boundp obj 'key) + (error "No key for %s" (oref obj command)))) + +(cl-defmethod transient--init-suffix-key ((obj transient-argument)) + (if (transient-switches--eieio-childp obj) + (cl-call-next-method obj) + (unless (slot-boundp obj 'shortarg) + (when-let ((shortarg (transient--derive-shortarg (oref obj argument)))) + (oset obj shortarg shortarg))) + (unless (slot-boundp obj 'key) + (if (slot-boundp obj 'shortarg) + (oset obj key (oref obj shortarg)) + (error "No key for %s" (oref obj command)))))) + +(defun transient--use-level-p (level &optional edit) + (or transient--all-levels-p + (and transient--editp (not edit)) + (and (>= level 1) + (<= level (oref transient--prefix level))))) + +(defun transient--use-suffix-p (obj) + (let ((transient--shadowed-buffer (current-buffer)) + (transient--pending-suffix obj)) + (transient--do-suffix-p + (oref obj if) + (oref obj if-not) + (oref obj if-nil) + (oref obj if-non-nil) + (oref obj if-mode) + (oref obj if-not-mode) + (oref obj if-derived) + (oref obj if-not-derived) + t))) + +(defun transient--inapt-suffix-p (obj) + (let ((transient--shadowed-buffer (current-buffer)) + (transient--pending-suffix obj)) + (transient--do-suffix-p + (oref obj inapt-if) + (oref obj inapt-if-not) + (oref obj inapt-if-nil) + (oref obj inapt-if-non-nil) + (oref obj inapt-if-mode) + (oref obj inapt-if-not-mode) + (oref obj inapt-if-derived) + (oref obj inapt-if-not-derived) + nil))) + +(defun transient--do-suffix-p + (if if-not if-nil if-non-nil if-mode if-not-mode if-derived if-not-derived + default) + (cond + (if (funcall if)) + (if-not (not (funcall if-not))) + (if-non-nil (symbol-value if-non-nil)) + (if-nil (not (symbol-value if-nil))) + (if-mode (if (atom if-mode) + (eq major-mode if-mode) + (memq major-mode if-mode))) + (if-not-mode (not (if (atom if-not-mode) + (eq major-mode if-not-mode) + (memq major-mode if-not-mode)))) + (if-derived (if (or (atom if-derived) + (>= emacs-major-version 30)) + (derived-mode-p if-derived) + (apply #'derived-mode-p if-derived))) + (if-not-derived (not (if (or (atom if-not-derived) + (>= emacs-major-version 30)) + (derived-mode-p if-not-derived) + (apply #'derived-mode-p if-not-derived)))) + (default))) + +(defun transient--suffix-predicate (spec) + (let ((plist (nth 2 spec))) + (seq-some (lambda (prop) + (and-let* ((pred (plist-get plist prop))) + (list prop pred))) + '( :if :if-not + :if-nil :if-non-nil + :if-mode :if-not-mode + :if-derived :if-not-derived + :inapt-if :inapt-if-not + :inapt-if-nil :inapt-if-non-nil + :inapt-if-mode :inapt-if-not-mode + :inapt-if-derived :inapt-if-not-derived)))) + +;;; Flow-Control + +(defun transient--init-transient () + (transient--debug 'init-transient) + (transient--push-keymap 'transient--transient-map) + (transient--push-keymap 'transient--redisplay-map) + (add-hook 'pre-command-hook #'transient--pre-command) + (add-hook 'post-command-hook #'transient--post-command) + (advice-add 'recursive-edit :around #'transient--recursive-edit) + (when transient--exitp + ;; This prefix command was invoked as the suffix of another. + ;; Prevent `transient--post-command' from removing the hooks + ;; that we just added. + (setq transient--exitp 'replace))) + +(defun transient--refresh-transient () + (transient--debug 'refresh-transient) + (transient--pop-keymap 'transient--predicate-map) + (transient--pop-keymap 'transient--transient-map) + (transient--pop-keymap 'transient--redisplay-map) + (transient--init-objects) + (transient--init-keymaps) + (transient--push-keymap 'transient--transient-map) + (transient--push-keymap 'transient--redisplay-map) + (transient--redisplay)) + +(defun transient--pre-command () + (transient--debug 'pre-command) + (transient--with-emergency-exit :pre-command + ;; The use of `overriding-terminal-local-map' does not prevent the + ;; lookup of command remappings in the overridden maps, which can + ;; lead to a suffix being remapped to a non-suffix. We have to undo + ;; the remapping in that case. However, remapping a non-suffix to + ;; another should remain possible. + (when (and (transient--get-pre-command this-original-command 'suffix) + (not (transient--get-pre-command this-command 'suffix))) + (setq this-command this-original-command)) + (cond + ((memq this-command '(transient-update transient-quit-seq)) + (transient--pop-keymap 'transient--redisplay-map)) + ((and transient--helpp + (not (memq this-command '(transient-quit-one + transient-quit-all)))) + (cond + ((transient-help) + (transient--do-suspend) + (setq this-command 'transient-suspend) + (transient--pre-exit)) + ((not (transient--edebug-command-p)) + (setq this-command 'transient-undefined)))) + ((and transient--editp + (transient-suffix-object) + (not (memq this-command '(transient-quit-one + transient-quit-all + transient-help)))) + (setq this-command 'transient-set-level) + (transient--wrap-command)) + (t + (setq transient--exitp nil) + (let ((exitp (eq (transient--call-pre-command) transient--exit))) + (transient--wrap-command) + (when exitp + (transient--pre-exit))))))) + +(defun transient--pre-exit () + (transient--debug 'pre-exit) + (transient--delete-window) + (transient--timer-cancel) + (transient--pop-keymap 'transient--transient-map) + (transient--pop-keymap 'transient--redisplay-map) + (unless transient--showp + (let ((message-log-max nil)) + (message ""))) + (setq transient--transient-map nil) + (setq transient--predicate-map nil) + (setq transient--redisplay-map nil) + (setq transient--redisplay-key nil) + (setq transient--helpp nil) + (setq transient--editp nil) + (setq transient--prefix nil) + (setq transient--layout nil) + (setq transient--suffixes nil) + (setq transient--original-window nil) + (setq transient--original-buffer nil) + (setq transient--window nil)) + +(defun transient--delete-window () + (when (window-live-p transient--window) + (let ((remain-in-minibuffer-window + (and (minibuffer-selected-window) + (selected-window)))) + ;; Only delete the window if it has never shown another buffer. + (unless (eq (car (window-parameter transient--window 'quit-restore)) + 'other) + (with-demoted-errors "Error while exiting transient: %S" + (delete-window transient--window))) + (when (buffer-live-p transient--buffer) + (kill-buffer transient--buffer)) + (setq transient--buffer nil) + (when remain-in-minibuffer-window + (select-window remain-in-minibuffer-window))))) + +(defun transient--export () + (setq transient-current-prefix transient--prefix) + (setq transient-current-command (oref transient--prefix command)) + (setq transient-current-suffixes transient--suffixes) + (transient--history-push transient--prefix)) + +(defun transient--suspend-override (&optional nohide) + (transient--debug 'suspend-override) + (transient--timer-cancel) + (cond ((and (not nohide) transient-hide-during-minibuffer-read) + (transient--delete-window)) + ((and transient--prefix transient--redisplay-key) + (setq transient--redisplay-key nil) + (when transient--showp + (if-let ((win (minibuffer-selected-window))) + (with-selected-window win + (transient--show)) + (transient--show))))) + (transient--pop-keymap 'transient--transient-map) + (transient--pop-keymap 'transient--redisplay-map) + (remove-hook 'pre-command-hook #'transient--pre-command) + (remove-hook 'post-command-hook #'transient--post-command)) + +(defun transient--resume-override (&optional _ignore) + (transient--debug 'resume-override) + (when (and transient--showp transient-hide-during-minibuffer-read) + (transient--show)) + (transient--push-keymap 'transient--transient-map) + (transient--push-keymap 'transient--redisplay-map) + (add-hook 'pre-command-hook #'transient--pre-command) + (add-hook 'post-command-hook #'transient--post-command)) + +(defun transient--recursive-edit (fn) + (transient--debug 'recursive-edit) + (if (not transient--prefix) + (funcall fn) + (transient--suspend-override (bound-and-true-p edebug-active)) + (funcall fn) ; Already unwind protected. + (cond ((memq this-command '(top-level abort-recursive-edit)) + (setq transient--exitp t) + (transient--post-exit) + (transient--delete-window)) + (transient--prefix + (transient--resume-override))))) + +(defmacro transient--with-suspended-override (&rest body) + (let ((depth (make-symbol "depth")) + (setup (make-symbol "setup")) + (exit (make-symbol "exit"))) + `(if (and transient--transient-map + (memq transient--transient-map + overriding-terminal-local-map)) + (let ((,depth (1+ (minibuffer-depth))) ,setup ,exit) + (setq ,setup + (lambda () "@transient--with-suspended-override" + (transient--debug 'minibuffer-setup) + (remove-hook 'minibuffer-setup-hook ,setup) + (transient--suspend-override))) + (setq ,exit + (lambda () "@transient--with-suspended-override" + (transient--debug 'minibuffer-exit) + (when (= (minibuffer-depth) ,depth) + (transient--resume-override)))) + (unwind-protect + (progn + (add-hook 'minibuffer-setup-hook ,setup) + (add-hook 'minibuffer-exit-hook ,exit) + ,@body) + (remove-hook 'minibuffer-setup-hook ,setup) + (remove-hook 'minibuffer-exit-hook ,exit))) + ,@body))) + +(static-if (>= emacs-major-version 30) ;transient--wrap-command + (defun transient--wrap-command () + (cl-assert + (>= emacs-major-version 30) nil + "Emacs was downgraded, making it necessary to recompile Transient") + (letrec + ((prefix transient--prefix) + (suffix this-command) + (advice + (lambda (fn &rest args) + (interactive + (lambda (spec) + (let ((abort t)) + (unwind-protect + (prog1 (let ((debugger #'transient--exit-and-debug)) + (advice-eval-interactive-spec spec)) + (setq abort nil)) + (when abort + (when-let ((unwind (oref prefix unwind-suffix))) + (transient--debug 'unwind-interactive) + (funcall unwind suffix)) + (advice-remove suffix advice) + (oset prefix unwind-suffix nil)))))) + (unwind-protect + (let ((debugger #'transient--exit-and-debug)) + (apply fn args)) + (when-let ((unwind (oref prefix unwind-suffix))) + (transient--debug 'unwind-command) + (funcall unwind suffix)) + (advice-remove suffix advice) + (oset prefix unwind-suffix nil))))) + (when (symbolp this-command) + (advice-add suffix :around advice '((depth . -99)))))) + + (defun transient--wrap-command () + (let* ((prefix transient--prefix) + (suffix this-command) + (advice nil) + (advice-interactive + (lambda (spec) + (let ((abort t)) + (unwind-protect + (prog1 (let ((debugger #'transient--exit-and-debug)) + (advice-eval-interactive-spec spec)) + (setq abort nil)) + (when abort + (when-let ((unwind (oref prefix unwind-suffix))) + (transient--debug 'unwind-interactive) + (funcall unwind suffix)) + (advice-remove suffix advice) + (oset prefix unwind-suffix nil)))))) + (advice-body + (lambda (fn &rest args) + (unwind-protect + (let ((debugger #'transient--exit-and-debug)) + (apply fn args)) + (when-let ((unwind (oref prefix unwind-suffix))) + (transient--debug 'unwind-command) + (funcall unwind suffix)) + (advice-remove suffix advice) + (oset prefix unwind-suffix nil))))) + (setq advice `(lambda (fn &rest args) + (interactive ,advice-interactive) + (apply ',advice-body fn args))) + (when (symbolp this-command) + (advice-add suffix :around advice '((depth . -99))))))) + +(defun transient--premature-post-command () + (and (equal (this-command-keys-vector) []) + (= (minibuffer-depth) + (1+ transient--minibuffer-depth)) + (progn + (transient--debug 'premature-post-command) + (transient--suspend-override) + (oset (or transient--prefix transient-current-prefix) + unwind-suffix + (if transient--exitp + #'transient--post-exit + #'transient--resume-override)) + t))) + +(defun transient--post-command () + (unless (transient--premature-post-command) + (transient--debug 'post-command) + (transient--with-emergency-exit :post-command + (cond (transient--exitp (transient--post-exit)) + ;; If `this-command' is the current transient prefix, then we + ;; have already taken care of updating the transient buffer... + ((and (eq this-command (oref transient--prefix command)) + ;; ... but if `prefix-arg' is non-nil, then the values + ;; of `this-command' and `real-this-command' are untrue + ;; because `prefix-command-preserve-state' changes them. + ;; We cannot use `current-prefix-arg' because it is set + ;; too late (in `command-execute'), and if it were set + ;; earlier, then we likely still would not be able to + ;; rely on it, and `prefix-command-preserve-state-hook' + ;; would have to be used to record that a universal + ;; argument is in effect. + (not prefix-arg))) + (transient--refreshp + (transient--refresh-transient)) + ((let ((old transient--redisplay-map) + (new (transient--make-redisplay-map))) + (unless (equal old new) + (transient--pop-keymap 'transient--redisplay-map) + (setq transient--redisplay-map new) + (transient--push-keymap 'transient--redisplay-map)) + (transient--redisplay))))))) + +(defun transient--post-exit (&optional command) + (transient--debug 'post-exit) + (unless (and (eq transient--exitp 'replace) + (or transient--prefix + ;; The current command could act as a prefix, + ;; but decided not to call `transient-setup', + ;; or it is prevented from doing so because it + ;; uses the minibuffer and the user aborted + ;; that. + (prog1 nil + (if (let ((obj (transient-suffix-object command))) + (and (slot-boundp obj 'transient) + (oref obj transient))) + ;; This sub-prefix is a transient suffix; + ;; go back to outer prefix, by calling + ;; `transient--stack-pop' further down. + (setq transient--exitp nil) + (transient--stack-zap))))) + (remove-hook 'pre-command-hook #'transient--pre-command) + (remove-hook 'post-command-hook #'transient--post-command) + (advice-remove 'recursive-edit #'transient--recursive-edit)) + (setq transient-current-prefix nil) + (setq transient-current-command nil) + (setq transient-current-suffixes nil) + (let ((resume (and transient--stack + (not (memq transient--exitp '(replace suspend)))))) + (unless (or resume (eq transient--exitp 'replace)) + (setq transient--showp nil)) + (setq transient--exitp nil) + (setq transient--helpp nil) + (setq transient--editp nil) + (setq transient--all-levels-p nil) + (setq transient--minibuffer-depth 0) + (run-hooks 'transient-exit-hook) + (when resume + (transient--stack-pop)))) + +(defun transient--stack-push () + (transient--debug 'stack-push) + (push (list (oref transient--prefix command) + transient--layout + transient--editp + :transient-suffix (oref transient--prefix transient-suffix) + :scope (oref transient--prefix scope)) + transient--stack)) + +(defun transient--stack-pop () + (transient--debug 'stack-pop) + (and transient--stack + (prog1 t (apply #'transient-setup (pop transient--stack))))) + +(defun transient--stack-zap () + (transient--debug 'stack-zap) + (setq transient--stack nil)) + +(defun transient--redisplay () + (if (or (eq transient-show-popup t) + transient--showp) + (unless + (or (memq this-command transient--scroll-commands) + (and (or (memq this-command '(mouse-drag-region + mouse-set-region)) + (equal (key-description (this-command-keys-vector)) + "<mouse-movement>")) + (and (eq (current-buffer) transient--buffer)))) + (transient--show)) + (when (and (numberp transient-show-popup) + (not (zerop transient-show-popup)) + (not transient--timer)) + (transient--timer-start)) + (transient--show-brief))) + +(defun transient--timer-start () + (setq transient--timer + (run-at-time (abs transient-show-popup) nil + (lambda () + (transient--timer-cancel) + (transient--show) + (let ((message-log-max nil)) + (message "")))))) + +(defun transient--timer-cancel () + (when transient--timer + (cancel-timer transient--timer) + (setq transient--timer nil))) + +(defun transient--debug (arg &rest args) + (when transient--debug + (let ((inhibit-message (not (eq transient--debug 'message)))) + (if (symbolp arg) + (message "-- %-22s (cmd: %s, event: %S, exit: %s%s)" + arg + (cond ((and (symbolp this-command) this-command)) + ((fboundp 'help-fns-function-name) + (help-fns-function-name this-command)) + ((byte-code-function-p this-command) + "#[...]") + (this-command)) + (key-description (this-command-keys-vector)) + transient--exitp + (cond ((keywordp (car args)) + (format ", from: %s" + (substring (symbol-name (car args)) 1))) + ((stringp (car args)) + (concat ", " (apply #'format args))) + ((functionp (car args)) + (concat ", " (apply (car args) (cdr args)))) + (""))) + (apply #'message arg args))))) + +(defun transient--emergency-exit (&optional id) + "Exit the current transient command after an error occurred. +When no transient is active (i.e., when `transient--prefix' is +nil) then do nothing. Optional ID is a keyword identifying the +exit." + (transient--debug 'emergency-exit id) + (when transient--prefix + (setq transient--stack nil) + (setq transient--exitp t) + (transient--pre-exit) + (transient--post-exit))) + +;;; Pre-Commands + +(defun transient--call-pre-command () + (if-let ((fn (transient--get-pre-command this-command))) + (let ((action (funcall fn))) + (when (eq action transient--exit) + (setq transient--exitp (or transient--exitp t))) + action) + (if (let ((keys (this-command-keys-vector))) + (eq (aref keys (1- (length keys))) ?\C-g)) + (setq this-command 'transient-noop) + (unless (transient--edebug-command-p) + (setq this-command 'transient-undefined))) + transient--stay)) + +(defun transient--get-pre-command (&optional cmd enforce-type) + (or (and (not (eq enforce-type 'non-suffix)) + (symbolp cmd) + (lookup-key transient--predicate-map (vector cmd))) + (and (not (eq enforce-type 'suffix)) + (transient--resolve-pre-command + (oref transient--prefix transient-non-suffix) + t)))) + +(defun transient--resolve-pre-command (pre &optional resolve-boolean) + (cond ((booleanp pre) + (if resolve-boolean + (if pre #'transient--do-stay #'transient--do-warn) + pre)) + ((string-match-p "--do-" (symbol-name pre)) pre) + ((let ((sym (intern (format "transient--do-%s" pre)))) + (if (functionp sym) sym pre))))) + +(defun transient--do-stay () + "Call the command without exporting variables and stay transient." + transient--stay) + +(defun transient--do-noop () + "Call `transient-noop' and stay transient." + (setq this-command 'transient-noop) + transient--stay) + +(defun transient--do-warn () + "Call `transient-undefined' and stay transient." + (setq this-command 'transient-undefined) + transient--stay) + +(defun transient--do-warn-inapt () + "Call `transient-inapt' and stay transient." + (setq this-command 'transient-inapt) + transient--stay) + +(defun transient--do-call () + "Call the command after exporting variables and stay transient." + (transient--export) + transient--stay) + +(defun transient--do-return () + "Call the command after exporting variables and return to parent prefix. +If there is no parent prefix, then behave like `transient--do-exit'." + (if (not transient--stack) + (transient--do-exit) + (transient--export) + transient--exit)) + +(defun transient--do-exit () + "Call the command after exporting variables and exit the transient." + (transient--export) + (transient--stack-zap) + transient--exit) + +(defun transient--do-leave () + "Call the command without exporting variables and exit the transient." + (transient--stack-zap) + transient--exit) + +(defun transient--do-push-button () + "Call the command represented by the activated button. +Use that command's pre-command to determine transient behavior." + (if (and (mouse-event-p last-command-event) + (not (eq (posn-window (event-start last-command-event)) + transient--window))) + transient--stay + (setq this-command + (with-selected-window transient--window + (get-text-property (if (mouse-event-p last-command-event) + (posn-point (event-start last-command-event)) + (point)) + 'command))) + (transient--call-pre-command))) + +(defun transient--do-recurse () + "Call the transient prefix command, preparing for return to active transient. +If there is no parent prefix, then just call the command." + (transient--do-stack)) + +(defun transient--setup-recursion (prefix-obj) + (when transient--stack + (let ((command (oref prefix-obj command))) + (when-let ((suffix-obj (transient-suffix-object command))) + (when (memq (if (slot-boundp suffix-obj 'transient) + (oref suffix-obj transient) + (oref transient-current-prefix transient-suffix)) + (list t #'transient--do-recurse)) + (oset prefix-obj transient-suffix t)))))) + +(defun transient--do-stack () + "Call the transient prefix command, stacking the active transient. +Push the active transient to the transient stack." + (transient--export) + (transient--stack-push) + (setq transient--exitp 'replace) + transient--exit) + +(defun transient--do-replace () + "Call the transient prefix command, replacing the active transient. +Do not push the active transient to the transient stack." + (transient--export) + (setq transient--exitp 'replace) + transient--exit) + +(defun transient--do-suspend () + "Suspend the active transient, saving the transient stack." + (transient--stack-push) + (setq transient--exitp 'suspend) + transient--exit) + +(defun transient--do-quit-one () + "If active, quit help or edit mode, else exit the active transient." + (cond (transient--helpp + (setq transient--helpp nil) + transient--stay) + (transient--editp + (setq transient--editp nil) + (transient-setup) + transient--stay) + (prefix-arg + transient--stay) + (transient--exit))) + +(defun transient--do-quit-all () + "Exit all transients without saving the transient stack." + (transient--stack-zap) + transient--exit) + +(defun transient--do-move () + "Call the command if `transient-enable-popup-navigation' is non-nil. +In that case behave like `transient--do-stay', otherwise similar +to `transient--do-warn'." + (unless transient-enable-popup-navigation + (setq this-command 'transient-inhibit-move)) + transient--stay) + +(defun transient--do-minus () + "Call `negative-argument' or pivot to `transient-update'. +If `negative-argument' is invoked using \"-\" then preserve the +prefix argument and pivot to `transient-update'." + (when (equal (this-command-keys) "-") + (setq this-command 'transient-update)) + transient--stay) + +(put 'transient--do-stay 'transient-face 'transient-key-stay) +(put 'transient--do-noop 'transient-face 'transient-key-noop) +(put 'transient--do-warn 'transient-face 'transient-key-noop) +(put 'transient--do-warn-inapt 'transient-face 'transient-key-noop) +(put 'transient--do-call 'transient-face 'transient-key-stay) +(put 'transient--do-return 'transient-face 'transient-key-return) +(put 'transient--do-exit 'transient-face 'transient-key-exit) +(put 'transient--do-leave 'transient-face 'transient-key-exit) + +(put 'transient--do-recurse 'transient-face 'transient-key-stay) +(put 'transient--do-stack 'transient-face 'transient-key-stay) +(put 'transient--do-replace 'transient-face 'transient-key-exit) +(put 'transient--do-suspend 'transient-face 'transient-key-exit) + +(put 'transient--do-quit-one 'transient-face 'transient-key-return) +(put 'transient--do-quit-all 'transient-face 'transient-key-exit) +(put 'transient--do-move 'transient-face 'transient-key-stay) +(put 'transient--do-minus 'transient-face 'transient-key-stay) + +;;; Commands +;;;; Noop + +(defun transient-noop () + "Do nothing at all." + (interactive)) + +(defun transient-undefined () + "Warn the user that the pressed key is not bound to any suffix." + (interactive) + (transient--invalid "Unbound suffix")) + +(defun transient-inapt () + "Warn the user that the invoked command is inapt." + (interactive) + (transient--invalid "Inapt command")) + +(defun transient--invalid (msg) + (ding) + (message "%s: `%s' (Use `%s' to abort, `%s' for help)%s" + msg + (propertize (key-description (this-single-command-keys)) + 'face 'font-lock-warning-face) + (propertize "C-g" 'face 'transient-key) + (propertize "?" 'face 'transient-key) + ;; `this-command' is `transient-undefined' or `transient-inapt'. + ;; Show the command (`this-original-command') the user actually + ;; tried to invoke. + (if-let ((cmd (or (ignore-errors (symbol-name this-original-command)) + (ignore-errors (symbol-name this-command))))) + (format " [%s]" (propertize cmd 'face 'font-lock-warning-face)) + "")) + (unless (and transient--transient-map + (memq transient--transient-map overriding-terminal-local-map)) + (let ((transient--prefix (or transient--prefix 'sic))) + (transient--emergency-exit)) + (view-lossage) + (other-window 1) + (display-warning 'transient "Inconsistent transient state detected. +This should never happen. +Please open an issue and post the shown command log." :error))) + +(defun transient-inhibit-move () + "Warn the user that popup navigation is disabled." + (interactive) + (message "To enable use of `%s', please customize `%s'" + this-original-command + 'transient-enable-popup-navigation)) + +;;;; Core + +(defun transient-quit-all () + "Exit all transients without saving the transient stack." + (interactive)) + +(defun transient-quit-one () + "Exit the current transients, returning to outer transient, if any." + (interactive)) + +(defun transient-quit-seq () + "Abort the current incomplete key sequence." + (interactive)) + +(defun transient-update () + "Redraw the transient's state in the popup buffer." + (interactive) + (setq prefix-arg current-prefix-arg)) + +(defun transient-show () + "Show the transient's state in the popup buffer." + (interactive) + (setq transient--showp t)) + +(defun transient-push-button () + "Invoke the suffix command represented by this button." + (interactive)) + +;;;; Suspend + +(defun transient-suspend () + "Suspend the current transient. +It can later be resumed using `transient-resume', while no other +transient is active." + (interactive)) + +(define-minor-mode transient-resume-mode + "Auxiliary minor-mode used to resume a transient after viewing help.") + +(defun transient-resume () + "Resume a previously suspended stack of transients." + (interactive) + (cond (transient--stack + (let ((winconf transient--restore-winconf)) + (kill-local-variable 'transient--restore-winconf) + (when transient-resume-mode + (transient-resume-mode -1) + (quit-window)) + (when winconf + (set-window-configuration winconf))) + (transient--stack-pop)) + (transient-resume-mode + (kill-local-variable 'transient--restore-winconf) + (transient-resume-mode -1) + (quit-window)) + (t + (message "No suspended transient command")))) + +;;;; Help + +(defun transient-help (&optional interactive) + "Show help for the active transient or one of its suffixes.\n\n(fn)" + (interactive (list t)) + (if interactive + (setq transient--helpp t) + (with-demoted-errors "transient-help: %S" + (when (lookup-key transient--transient-map + (this-single-command-raw-keys)) + (setq transient--helpp nil) + (let ((winconf (current-window-configuration))) + (transient-show-help + (if (eq this-original-command 'transient-help) + transient--prefix + (or (transient-suffix-object) + this-original-command))) + (setq-local transient--restore-winconf winconf)) + (fit-window-to-buffer nil (frame-height) (window-height)) + (transient-resume-mode) + (message (substitute-command-keys + "Type \\`q' to resume transient command.")) + t)))) + +;;;; Level + +(defun transient-set-level (&optional command level) + "Set the level of the transient or one of its suffix commands." + (interactive + (let ((command this-original-command) + (prefix (oref transient--prefix command))) + (and (or (not (eq command 'transient-set-level)) + (and transient--editp + (setq command prefix))) + (list command + (let ((keys (this-single-command-raw-keys))) + (and (lookup-key transient--transient-map keys) + (progn + (transient--show) + (string-to-number + (transient--read-number-N + (format "Set level for `%s': " command) + nil nil (not (eq command prefix))))))))))) + (cond + ((not command) + (setq transient--editp t) + (transient-setup)) + (level + (let* ((prefix (oref transient--prefix command)) + (alist (alist-get prefix transient-levels)) + (akey command)) + (cond ((eq command prefix) + (oset transient--prefix level level) + (setq akey t)) + (t + (oset (transient-suffix-object command) level level) + (when (cdr (cl-remove-if-not (lambda (obj) + (eq (oref obj command) command)) + transient--suffixes)) + (setq akey (cons command (this-command-keys)))))) + (setf (alist-get akey alist) level) + (setf (alist-get prefix transient-levels) alist)) + (transient-save-levels) + (transient--show)) + (t + (transient-undefined)))) + +(transient-define-suffix transient-toggle-level-limit () + "Toggle whether to temporarily displayed suffixes on all levels." + :description + (lambda () + (cond + ((= transient-default-level transient--max-level) + "Always displaying all levels") + (transient--all-levels-p + (format "Hide suffix %s" + (propertize + (format "levels > %s" (oref (transient-prefix-object) level)) + 'face 'transient-higher-level))) + ("Show all suffix levels"))) + :inapt-if (lambda () (= transient-default-level transient--max-level)) + :transient t + (interactive) + (setq transient--all-levels-p (not transient--all-levels-p)) + (setq transient--refreshp t)) + +;;;; Value + +(defun transient-set () + "Set active transient's value for this Emacs session." + (interactive) + (transient-set-value (transient-prefix-object))) + +(defalias 'transient-set-and-exit #'transient-set + "Set active transient's value for this Emacs session and exit.") + +(defun transient-save () + "Save active transient's value for this and future Emacs sessions." + (interactive) + (transient-save-value (transient-prefix-object))) + +(defalias 'transient-save-and-exit #'transient-save + "Save active transient's value for this and future Emacs sessions and exit.") + +(defun transient-reset () + "Clear the set and saved values of the active transient." + (interactive) + (transient-reset-value (transient-prefix-object))) + +(defun transient-history-next () + "Switch to the next value used for the active transient." + (interactive) + (let* ((obj transient--prefix) + (pos (1- (oref obj history-pos))) + (hst (oref obj history))) + (if (< pos 0) + (user-error "End of history") + (oset obj history-pos pos) + (oset obj value (nth pos hst)) + (mapc #'transient-init-value transient--suffixes)))) + +(defun transient-history-prev () + "Switch to the previous value used for the active transient." + (interactive) + (let* ((obj transient--prefix) + (pos (1+ (oref obj history-pos))) + (hst (oref obj history)) + (len (length hst))) + (if (> pos (1- len)) + (user-error "End of history") + (oset obj history-pos pos) + (oset obj value (nth pos hst)) + (mapc #'transient-init-value transient--suffixes)))) + +;;;; Auxiliary + +(defun transient-toggle-common () + "Toggle whether common commands are permanently shown." + (interactive) + (setq transient-show-common-commands (not transient-show-common-commands))) + +(defun transient-toggle-debug () + "Toggle debugging statements for transient commands." + (interactive) + (setq transient--debug (not transient--debug)) + (message "Debugging transient %s" + (if transient--debug "enabled" "disabled"))) + +(transient-define-suffix transient-echo-arguments (arguments) + "Show the transient's active ARGUMENTS in the echo area. +Intended for use in prefixes used for demonstration purposes, +such as when suggesting a new feature or reporting an issue." + :transient t + :description "Echo arguments" + :key "x" + (interactive (list (transient-args transient-current-command))) + (message "%s: %s" + (key-description (this-command-keys)) + (mapconcat (lambda (arg) + (propertize (if (string-match-p " " arg) + (format "%S" arg) + arg) + 'face 'transient-argument)) + arguments " "))) + +;;; Value +;;;; Init + +(cl-defgeneric transient-init-scope (obj) + "Set the scope of the suffix object OBJ. + +The scope is actually a property of the transient prefix, not of +individual suffixes. However it is possible to invoke a suffix +command directly instead of from a transient. In that case, if +the suffix expects a scope, then it has to determine that itself +and store it in its `scope' slot. + +This function is called for all suffix commands, but unless a +concrete method is implemented this falls through to the default +implementation, which is a noop.") + +(cl-defmethod transient-init-scope ((_ transient-suffix)) + "Noop." nil) + +(cl-defgeneric transient-init-value (_) + "Set the initial value of the object OBJ. + +This function is called for all prefix and suffix commands. + +For suffix commands (including infix argument commands) the +default implementation is a noop. Classes derived from the +abstract `transient-infix' class must implement this function. +Non-infix suffix commands usually don't have a value." + nil) + +(cl-defmethod transient-init-value :around ((obj transient-prefix)) + "If bound, then call OBJ's `init-value' function. +Otherwise call the primary method according to object's class." + (if (slot-boundp obj 'init-value) + (funcall (oref obj init-value) obj) + (cl-call-next-method obj))) + +(cl-defmethod transient-init-value :around ((obj transient-infix)) + "If bound, then call OBJ's `init-value' function. +Otherwise call the primary method according to object's class." + (if (slot-boundp obj 'init-value) + (funcall (oref obj init-value) obj) + (cl-call-next-method obj))) + +(cl-defmethod transient-init-value ((obj transient-prefix)) + (if (slot-boundp obj 'value) + (oref obj value) + (oset obj value + (if-let ((saved (assq (oref obj command) transient-values))) + (cdr saved) + (transient-default-value obj))))) + +(cl-defmethod transient-init-value ((obj transient-argument)) + (oset obj value + (let ((value (oref transient--prefix value)) + (argument (and (slot-boundp obj 'argument) + (oref obj argument))) + (multi-value (oref obj multi-value)) + (case-fold-search nil) + (regexp (if (slot-exists-p obj 'argument-regexp) + (oref obj argument-regexp) + (format "\\`%s\\(.*\\)" (oref obj argument))))) + (if (memq multi-value '(t rest)) + (cdr (assoc argument value)) + (let ((match (lambda (v) + (and (stringp v) + (string-match regexp v) + (match-string 1 v))))) + (if multi-value + (delq nil (mapcar match value)) + (cl-some match value))))))) + +(cl-defmethod transient-init-value ((obj transient-switch)) + (oset obj value + (car (member (oref obj argument) + (oref transient--prefix value))))) + +;;;; Default + +(cl-defgeneric transient-default-value (_) + "Return the default value." + nil) + +(cl-defmethod transient-default-value ((obj transient-prefix)) + (if-let ((default (and (slot-boundp obj 'default-value) + (oref obj default-value)))) + (if (functionp default) + (funcall default) + default) + nil)) + +;;;; Read + +(cl-defgeneric transient-infix-read (obj) + "Determine the new value of the infix object OBJ. + +This function merely determines the value; `transient-infix-set' +is used to actually store the new value in the object. + +For most infix classes this is done by reading a value from the +user using the reader specified by the `reader' slot (using the +`transient-infix' method described below). + +For some infix classes the value is changed without reading +anything in the minibuffer, i.e., the mere act of invoking the +infix command determines what the new value should be, based +on the previous value.") + +(cl-defmethod transient-infix-read :around ((obj transient-infix)) + "Refresh the transient buffer and call the next method. + +Also wrap `cl-call-next-method' with two macros: +- `transient--with-suspended-override' allows use of minibuffer. +- `transient--with-emergency-exit' arranges for the transient to + be exited in case of an error." + (transient--show) + (transient--with-emergency-exit :infix-read + (transient--with-suspended-override + (cl-call-next-method obj)))) + +(cl-defmethod transient-infix-read ((obj transient-infix)) + "Read a value while taking care of history. + +This method is suitable for a wide variety of infix commands, +including but not limited to inline arguments and variables. + +If you do not use this method for your own infix class, then +you should likely replicate a lot of the behavior of this +method. If you fail to do so, then users might not appreciate +the lack of history, for example. + +Only for very simple classes that toggle or cycle through a very +limited number of possible values should you replace this with a +simple method that does not handle history. (E.g., for a command +line switch the only possible values are \"use it\" and \"don't use +it\", in which case it is pointless to preserve history.)" + (with-slots (value multi-value always-read allow-empty choices) obj + (if (and value + (not multi-value) + (not always-read) + transient--prefix) + (oset obj value nil) + (let* ((enable-recursive-minibuffers t) + (reader (oref obj reader)) + (choices (if (functionp choices) (funcall choices) choices)) + (prompt (transient-prompt obj)) + (value (if multi-value (mapconcat #'identity value ",") value)) + (history-key (or (oref obj history-key) + (oref obj command))) + (transient--history (alist-get history-key transient-history)) + (transient--history (if (or (null value) + (eq value (car transient--history))) + transient--history + (cons value transient--history))) + (initial-input (and transient-read-with-initial-input + (car transient--history))) + (history (if initial-input + (cons 'transient--history 1) + 'transient--history)) + (value + (cond + (reader (funcall reader prompt initial-input history)) + (multi-value + (completing-read-multiple prompt choices nil nil + initial-input history)) + (choices + (completing-read prompt choices nil t initial-input history)) + ((read-string prompt initial-input history))))) + (cond ((and (equal value "") (not allow-empty)) + (setq value nil)) + ((and (equal value "\"\"") allow-empty) + (setq value ""))) + (when value + (when (and (bound-and-true-p ivy-mode) + (stringp (car transient--history))) + (set-text-properties 0 (length (car transient--history)) nil + (car transient--history))) + (setf (alist-get history-key transient-history) + (delete-dups transient--history))) + value)))) + +(cl-defmethod transient-infix-read ((obj transient-switch)) + "Toggle the switch on or off." + (if (oref obj value) nil (oref obj argument))) + +(cl-defmethod transient-infix-read ((obj transient-switches)) + "Cycle through the mutually exclusive switches. +The last value is \"don't use any of these switches\"." + (let ((choices (mapcar (apply-partially #'format (oref obj argument-format)) + (oref obj choices)))) + (if-let ((value (oref obj value))) + (cadr (member value choices)) + (car choices)))) + +(cl-defmethod transient-infix-read ((command symbol)) + "Elsewhere use the reader of the infix command COMMAND. +Use this if you want to share an infix's history with a regular +stand-alone command." + (if-let ((obj (transient--suffix-prototype command))) + (cl-letf (((symbol-function #'transient--show) #'ignore)) + (transient-infix-read obj)) + (error "Not a suffix command: `%s'" command))) + +;;;; Readers + +(defun transient-read-file (prompt _initial-input _history) + "Read a file." + (file-local-name (expand-file-name (read-file-name prompt)))) + +(defun transient-read-existing-file (prompt _initial-input _history) + "Read an existing file." + (file-local-name (expand-file-name (read-file-name prompt nil nil t)))) + +(defun transient-read-directory (prompt _initial-input _history) + "Read a directory." + (file-local-name (expand-file-name (read-directory-name prompt)))) + +(defun transient-read-existing-directory (prompt _initial-input _history) + "Read an existing directory." + (file-local-name (expand-file-name (read-directory-name prompt nil nil t)))) + +(defun transient-read-number-N0 (prompt initial-input history) + "Read a natural number (including zero) and return it as a string." + (transient--read-number-N prompt initial-input history t)) + +(defun transient-read-number-N+ (prompt initial-input history) + "Read a natural number (excluding zero) and return it as a string." + (transient--read-number-N prompt initial-input history nil)) + +(defun transient--read-number-N (prompt initial-input history include-zero) + (save-match-data + (cl-block nil + (while t + (let ((str (read-from-minibuffer prompt initial-input nil nil history))) + (when (or (string-equal str "") + (string-match-p (if include-zero + "\\`\\(0\\|[1-9][0-9]*\\)\\'" + "\\`[1-9][0-9]*\\'") + str)) + (cl-return str))) + (message "Please enter a natural number (%s zero)." + (if include-zero "including" "excluding")) + (sit-for 1))))) + +(defun transient-read-date (prompt default-time _history) + "Read a date using `org-read-date' (which see)." + (require 'org) + (when (fboundp 'org-read-date) + (org-read-date 'with-time nil nil prompt default-time))) + +;;;; Prompt + +(cl-defgeneric transient-prompt (obj) + "Return the prompt to be used to read infix object OBJ's value.") + +(cl-defmethod transient-prompt ((obj transient-infix)) + "Return the prompt to be used to read infix object OBJ's value. + +This implementation should be suitable for almost all infix +commands. + +If the value of OBJ's `prompt' slot is non-nil, then it must be +a string or a function. If it is a string, then use that. If +it is a function, then call that with OBJ as the only argument. +That function must return a string, which is then used as the +prompt. + +Otherwise, if the value of either the `argument' or `variable' +slot of OBJ is a string, then base the prompt on that (preferring +the former), appending either \"=\" (if it appears to be a +command-line option) or \": \". + +Finally fall through to using \"(BUG: no prompt): \" as the +prompt." + (if-let ((prompt (oref obj prompt))) + (let ((prompt (if (functionp prompt) + (funcall prompt obj) + prompt))) + (if (stringp prompt) + prompt + "(BUG: no prompt): ")) + (or (and-let* ((arg (and (slot-boundp obj 'argument) (oref obj argument)))) + (if (and (stringp arg) (string-suffix-p "=" arg)) + arg + (concat arg ": "))) + (and-let* ((var (and (slot-boundp obj 'variable) (oref obj variable)))) + (and (stringp var) + (concat var ": "))) + "(BUG: no prompt): "))) + +;;;; Set + +(cl-defgeneric transient-infix-set (obj value) + "Set the value of infix object OBJ to value.") + +(cl-defmethod transient-infix-set ((obj transient-infix) value) + "Set the value of infix object OBJ to value." + (oset obj value value)) + +(cl-defmethod transient-infix-set :after ((obj transient-argument) value) + "Unset incompatible infix arguments." + (when-let* ((value) + (val (transient-infix-value obj)) + (arg (if (slot-boundp obj 'argument) + (oref obj argument) + (oref obj argument-format))) + (spec (oref transient--prefix incompatible)) + (filter (lambda (x rule) + (and (member x rule) + (remove x rule)))) + (incomp (nconc + (cl-mapcan (apply-partially filter arg) spec) + (and (not (equal val arg)) + (cl-mapcan (apply-partially filter val) spec))))) + (dolist (obj transient--suffixes) + (when-let* (((cl-typep obj 'transient-argument)) + (val (transient-infix-value obj)) + (arg (if (slot-boundp obj 'argument) + (oref obj argument) + (oref obj argument-format))) + ((if (equal val arg) + (member arg incomp) + (or (member val incomp) + (member arg incomp))))) + (transient-infix-set obj nil))))) + +(cl-defgeneric transient-set-value (obj) + "Set the value of the transient prefix OBJ.") + +(cl-defmethod transient-set-value ((obj transient-prefix)) + (oset (oref obj prototype) value (transient-get-value)) + (transient--history-push obj)) + +;;;; Save + +(cl-defgeneric transient-save-value (obj) + "Save the value of the transient prefix OBJ.") + +(cl-defmethod transient-save-value ((obj transient-prefix)) + (let ((value (transient-get-value))) + (oset (oref obj prototype) value value) + (setf (alist-get (oref obj command) transient-values) value) + (transient-save-values)) + (transient--history-push obj)) + +;;;; Reset + +(cl-defgeneric transient-reset-value (obj) + "Clear the set and saved values of the transient prefix OBJ.") + +(cl-defmethod transient-reset-value ((obj transient-prefix)) + (let ((value (transient-default-value obj))) + (oset obj value value) + (oset (oref obj prototype) value value) + (setf (alist-get (oref obj command) transient-values nil 'remove) nil) + (transient-save-values)) + (transient--history-push obj) + (mapc #'transient-init-value transient--suffixes)) + +;;;; Get + +(defun transient-args (prefix) + "Return the value of the transient prefix command PREFIX. +If the current command was invoked from the transient prefix +command PREFIX, then return the active infix arguments. If +the current command was not invoked from PREFIX, then return +the set, saved or default value for PREFIX." + (cl-mapcan #'transient--get-wrapped-value (transient-suffixes prefix))) + +(defun transient-suffixes (prefix) + "Return the suffix objects of the transient prefix command PREFIX." + (if (eq transient-current-command prefix) + transient-current-suffixes + (let ((transient--prefix (transient--init-prefix prefix))) + (transient--flatten-suffixes + (transient--init-suffixes prefix))))) + +(defun transient-get-value () + (transient--with-emergency-exit :get-value + (cl-mapcan (lambda (obj) + (and (or (not (slot-exists-p obj 'unsavable)) + (not (oref obj unsavable))) + (transient--get-wrapped-value obj))) + transient-current-suffixes))) + +(defun transient--get-wrapped-value (obj) + (and-let* ((value (transient-infix-value obj))) + (pcase-exhaustive (and (slot-exists-p obj 'multi-value) + (oref obj multi-value)) + ('nil (list value)) + ((or 't 'rest) (list value)) + ('repeat value)))) + +(cl-defgeneric transient-infix-value (obj) + "Return the value of the suffix object OBJ. + +This function is called by `transient-args' (which see), meaning +this function is how the value of a transient is determined so +that the invoked suffix command can use it. + +Currently most values are strings, but that is not set in stone. +Nil is not a value, it means \"no value\". + +Usually only infixes have a value, but see the method for +`transient-suffix'.") + +(cl-defmethod transient-infix-value ((_ transient-suffix)) + "Return nil, which means \"no value\". + +Infix arguments contribute the transient's value while suffix +commands consume it. This function is called for suffixes anyway +because a command that both contributes to the transient's value +and also consumes it is not completely unconceivable. + +If you define such a command, then you must define a derived +class and implement this function because this default method +does nothing." nil) + +(cl-defmethod transient-infix-value ((obj transient-infix)) + "Return the value of OBJ's `value' slot." + (oref obj value)) + +(cl-defmethod transient-infix-value ((obj transient-option)) + "Return ARGUMENT and VALUE as a unit or nil if the latter is nil." + (and-let* ((value (oref obj value))) + (let ((arg (oref obj argument))) + (pcase-exhaustive (oref obj multi-value) + ('nil (concat arg value)) + ((or 't 'rest) (cons arg value)) + ('repeat (mapcar (lambda (v) (concat arg v)) value)))))) + +(cl-defmethod transient-infix-value ((_ transient-variable)) + "Return nil, which means \"no value\". + +Setting the value of a variable is done by, well, setting the +value of the variable. I.e., this is a side-effect and does +not contribute to the value of the transient." + nil) + +;;;; Utilities + +(defun transient-arg-value (arg args) + "Return the value of ARG as it appears in ARGS. + +For a switch return a boolean. For an option return the value as +a string, using the empty string for the empty value, or nil if +the option does not appear in ARGS." + (if (string-suffix-p "=" arg) + (save-match-data + (and-let* ((match (let ((case-fold-search nil) + (re (format "\\`%s\\(?:=\\(.+\\)\\)?\\'" + (substring arg 0 -1)))) + (cl-find-if (lambda (a) + (and (stringp a) + (string-match re a))) + args)))) + (or (match-string 1 match) ""))) + (and (member arg args) t))) + +(defun transient-scope () + "Return the value of the `scope' slot of the current prefix." + (oref (transient-prefix-object) scope)) + +;;; History + +(cl-defgeneric transient--history-key (obj) + "Return OBJ's history key. +If the value of the `history-key' slot is non-nil, then return +that. Otherwise return the value of the `command' slot." + (or (oref obj history-key) + (oref obj command))) + +(cl-defgeneric transient--history-push (obj) + "Push the current value of OBJ to its entry in `transient-history'." + (let ((key (transient--history-key obj))) + (setf (alist-get key transient-history) + (let ((args (transient-get-value))) + (cons args (delete args (alist-get key transient-history))))))) + +(cl-defgeneric transient--history-init (obj) + "Initialize OBJ's `history' slot. +This is the transient-wide history; many individual infixes also +have a history of their own.") + +(cl-defmethod transient--history-init ((obj transient-prefix)) + "Initialize OBJ's `history' slot from the variable `transient-history'." + (let ((val (oref obj value))) + (oset obj history + (cons val (delete val (alist-get (transient--history-key obj) + transient-history)))))) + +;;; Draw + +(defun transient--show-brief () + (let ((message-log-max nil)) + (if (and transient-show-popup (<= transient-show-popup 0)) + (message "%s-" (key-description (this-command-keys))) + (message + "%s- [%s] %s" + (key-description (this-command-keys)) + (oref transient--prefix command) + (mapconcat + #'identity + (sort + (cl-mapcan + (lambda (suffix) + (let ((key (kbd (oref suffix key)))) + ;; Don't list any common commands. + (and (not (memq (oref suffix command) + `(,(lookup-key transient-map key) + ,(lookup-key transient-sticky-map key) + ;; From transient-common-commands: + transient-set + transient-save + transient-history-prev + transient-history-next + transient-quit-one + transient-toggle-common + transient-set-level))) + (list (propertize (oref suffix key) 'face 'transient-key))))) + transient--suffixes) + #'string<) + (propertize "|" 'face 'transient-delimiter)))))) + +(defun transient--show () + (transient--timer-cancel) + (setq transient--showp t) + (let ((transient--shadowed-buffer (current-buffer)) + (focus nil)) + (setq transient--buffer (get-buffer-create transient--buffer-name)) + (with-current-buffer transient--buffer + (when transient-enable-popup-navigation + (setq focus (or (button-get (point) 'command) + (and (not (bobp)) + (button-get (1- (point)) 'command)) + (transient--heading-at-point)))) + (erase-buffer) + (run-hooks 'transient-setup-buffer-hook) + (when transient-force-fixed-pitch + (transient--force-fixed-pitch)) + (setq window-size-fixed t) + (when (bound-and-true-p tab-line-format) + (setq tab-line-format nil)) + (setq header-line-format nil) + (setq mode-line-format + (if (or (natnump transient-mode-line-format) + (eq transient-mode-line-format 'line)) + nil + transient-mode-line-format)) + (setq mode-line-buffer-identification + (symbol-name (oref transient--prefix command))) + (if transient-enable-popup-navigation + (setq-local cursor-in-non-selected-windows 'box) + (setq cursor-type nil)) + (setq display-line-numbers nil) + (setq show-trailing-whitespace nil) + (transient--insert-groups) + (when (or transient--helpp transient--editp) + (transient--insert-help)) + (when-let ((line (transient--separator-line))) + (insert line))) + (unless (window-live-p transient--window) + (setq transient--window + (display-buffer transient--buffer + transient-display-buffer-action))) + (when (window-live-p transient--window) + (with-selected-window transient--window + (goto-char (point-min)) + (when transient-enable-popup-navigation + (transient--goto-button focus)) + (transient--fit-window-to-buffer transient--window))))) + +(defun transient--fit-window-to-buffer (window) + (let ((window-resize-pixelwise t) + (window-size-fixed nil)) + (if (eq (car (window-parameter window 'quit-restore)) 'other) + ;; Grow but never shrink window that previously displayed + ;; another buffer and is going to display that again. + (fit-window-to-buffer window nil (window-height window)) + (fit-window-to-buffer window nil 1)))) + +(defun transient--separator-line () + (and-let* ((height (cond ((not window-system) nil) + ((natnump transient-mode-line-format) + transient-mode-line-format) + ((eq transient-mode-line-format 'line) 1))) + (face `(,@(and (>= emacs-major-version 27) '(:extend t)) + :background + ,(or (face-foreground (transient--key-face nil 'non-suffix) + nil t) + "#gray60")))) + (concat (propertize "__" 'face face 'display `(space :height (,height))) + (propertize "\n" 'face face 'line-height t)))) + +(defmacro transient-with-shadowed-buffer (&rest body) + "While in the transient buffer, temporarly make the shadowed buffer current." + (declare (indent 0) (debug t)) + `(with-current-buffer (or transient--shadowed-buffer (current-buffer)) + ,@body)) + +(defun transient--insert-groups () + (let ((groups (cl-mapcan (lambda (group) + (let ((hide (oref group hide))) + (and (not (and (functionp hide) + (transient-with-shadowed-buffer + (funcall hide)))) + (list group)))) + transient--layout))) + (while-let ((group (pop groups))) + (transient--insert-group group) + (when groups + (insert ?\n))))) + +(defvar transient--max-group-level 1) + +(cl-defgeneric transient--insert-group (group) + "Format GROUP and its elements and insert the result.") + +(cl-defmethod transient--insert-group :around ((group transient-group)) + "Insert GROUP's description, if any." + (when-let ((desc (transient-with-shadowed-buffer + (transient-format-description group)))) + (insert desc ?\n)) + (let ((transient--max-group-level + (max (oref group level) transient--max-group-level)) + (transient--pending-group group)) + (cl-call-next-method group))) + +(cl-defmethod transient--insert-group ((group transient-row)) + (transient--maybe-pad-keys group) + (dolist (suffix (oref group suffixes)) + (insert (transient-with-shadowed-buffer (transient-format suffix))) + (insert " ")) + (insert ?\n)) + +(cl-defmethod transient--insert-group ((group transient-column)) + (transient--maybe-pad-keys group) + (dolist (suffix (oref group suffixes)) + (let ((str (transient-with-shadowed-buffer (transient-format suffix)))) + (insert str) + (unless (string-match-p ".\n\\'" str) + (insert ?\n))))) + +(cl-defmethod transient--insert-group ((group transient-columns)) + (let* ((columns + (mapcar + (lambda (column) + (transient--maybe-pad-keys column group) + (transient-with-shadowed-buffer + (let* ((transient--pending-group column) + (rows (mapcar #'transient-format (oref column suffixes)))) + (if-let ((desc (transient-format-description column))) + (cons desc rows) + rows)))) + (oref group suffixes))) + (vp (or (oref transient--prefix variable-pitch) + transient-align-variable-pitch)) + (rs (apply #'max (mapcar #'length columns))) + (cs (length columns)) + (cw (mapcar (let ((widths (oref transient--prefix column-widths))) + (lambda (col) + (apply + #'max + (if-let ((min (pop widths))) + (if vp (* min (transient--pixel-width " ")) min) + 0) + (mapcar (if vp #'transient--pixel-width #'length) + col)))) + columns)) + (cc (transient--seq-reductions-from + (apply-partially #'+ (* 2 (if vp (transient--pixel-width " ") 1))) + cw 0))) + (if transient-force-single-column + (dotimes (c cs) + (dotimes (r rs) + (when-let ((cell (nth r (nth c columns)))) + (unless (equal cell "") + (insert cell ?\n)))) + (unless (= c (1- cs)) + (insert ?\n))) + (dotimes (r rs) + (dotimes (c cs) + (if vp + (progn + (when-let ((cell (nth r (nth c columns)))) + (insert cell)) + (if (= c (1- cs)) + (insert ?\n) + (insert (propertize " " 'display + `(space :align-to (,(nth (1+ c) cc))))))) + (when (> c 0) + (insert (make-string (max 1 (- (nth c cc) (current-column))) + ?\s))) + (when-let ((cell (nth r (nth c columns)))) + (insert cell)) + (when (= c (1- cs)) + (insert ?\n)))))))) + +(cl-defmethod transient--insert-group ((group transient-subgroups)) + (let ((subgroups (oref group suffixes))) + (while-let ((subgroup (pop subgroups))) + (transient--maybe-pad-keys subgroup group) + (transient--insert-group subgroup) + (when subgroups + (insert ?\n))))) + +(cl-defgeneric transient-format (obj) + "Format and return OBJ for display. + +When this function is called, then the current buffer is some +temporary buffer. If you need the buffer from which the prefix +command was invoked to be current, then do so by temporarily +making `transient--original-buffer' current.") + +(cl-defmethod transient-format ((arg string)) + "Return the string ARG after applying the `transient-heading' face." + (propertize arg 'face 'transient-heading)) + +(cl-defmethod transient-format ((_ null)) + "Return a string containing just the newline character." + "\n") + +(cl-defmethod transient-format ((arg integer)) + "Return a string containing just the ARG character." + (char-to-string arg)) + +(cl-defmethod transient-format :around ((obj transient-suffix)) + "Add additional formatting if appropriate. +When reading user input for this infix, then highlight it. +When edit-mode is enabled, then prepend the level information. +When `transient-enable-popup-navigation' is non-nil then format +as a button." + (let ((str (cl-call-next-method obj))) + (when (and (cl-typep obj 'transient-infix) + (eq (oref obj command) this-original-command) + (active-minibuffer-window)) + (setq str (transient--add-face str 'transient-active-infix))) + (when transient--editp + (setq str (concat (let ((level (oref obj level))) + (propertize (format " %s " level) + 'face (if (transient--use-level-p level t) + 'transient-enabled-suffix + 'transient-disabled-suffix))) + str))) + (when (and transient-enable-popup-navigation + (slot-boundp obj 'command)) + (setq str (make-text-button str nil + 'type 'transient + 'command (oref obj command)))) + str)) + +(cl-defmethod transient-format ((obj transient-infix)) + "Return a string generated using OBJ's `format'. +%k is formatted using `transient-format-key'. +%d is formatted using `transient-format-description'. +%v is formatted using `transient-format-value'." + (format-spec (oref obj format) + `((?k . ,(transient-format-key obj)) + (?d . ,(transient-format-description obj)) + (?v . ,(transient-format-value obj))))) + +(cl-defmethod transient-format ((obj transient-suffix)) + "Return a string generated using OBJ's `format'. +%k is formatted using `transient-format-key'. +%d is formatted using `transient-format-description'." + (format-spec (oref obj format) + `((?k . ,(transient-format-key obj)) + (?d . ,(transient-format-description obj))))) + +(cl-defgeneric transient-format-key (obj) + "Format OBJ's `key' for display and return the result.") + +(cl-defmethod transient-format-key :around ((obj transient-suffix)) + "Add `transient-inapt-suffix' face if suffix is inapt." + (let ((str (cl-call-next-method))) + (if (oref obj inapt) + (transient--add-face str 'transient-inapt-suffix) + str))) + +(cl-defmethod transient-format-key ((obj transient-suffix)) + "Format OBJ's `key' for display and return the result." + (let ((key (if (slot-boundp obj 'key) (oref obj key) "")) + (cmd (and (slot-boundp obj 'command) (oref obj command)))) + (when-let ((width (oref transient--pending-group pad-keys))) + (setq key (truncate-string-to-width key width nil ?\s))) + (if transient--redisplay-key + (let ((len (length transient--redisplay-key)) + (seq (cl-coerce (edmacro-parse-keys key t) 'list))) + (cond + ((member (seq-take seq len) + (list transient--redisplay-key + (thread-last transient--redisplay-key + (cl-substitute ?- 'kp-subtract) + (cl-substitute ?= 'kp-equal) + (cl-substitute ?+ 'kp-add)))) + (let ((pre (key-description (vconcat (seq-take seq len)))) + (suf (key-description (vconcat (seq-drop seq len))))) + (setq pre (string-replace "RET" "C-m" pre)) + (setq pre (string-replace "TAB" "C-i" pre)) + (setq suf (string-replace "RET" "C-m" suf)) + (setq suf (string-replace "TAB" "C-i" suf)) + ;; We use e.g., "-k" instead of the more correct "- k", + ;; because the former is prettier. If we did that in + ;; the definition, then we want to drop the space that + ;; is reinserted above. False-positives are possible + ;; for silly bindings like "-C-c C-c". + (unless (string-search " " key) + (setq pre (string-replace " " "" pre)) + (setq suf (string-replace " " "" suf))) + (concat (propertize pre 'face 'transient-unreachable-key) + (and (string-prefix-p (concat pre " ") key) " ") + (propertize suf 'face (transient--key-face cmd)) + (save-excursion + (and (string-match " +\\'" key) + (propertize (match-string 0 key) + 'face 'fixed-pitch)))))) + ((transient--lookup-key transient-sticky-map (kbd key)) + (propertize key 'face (transient--key-face cmd))) + (t + (propertize key 'face 'transient-unreachable-key)))) + (propertize key 'face (transient--key-face cmd))))) + +(cl-defmethod transient-format-key :around ((obj transient-argument)) + "Handle `transient-highlight-mismatched-keys'." + (let ((key (cl-call-next-method obj))) + (cond + ((not transient-highlight-mismatched-keys) key) + ((not (slot-boundp obj 'shortarg)) + (transient--add-face key 'transient-nonstandard-key)) + ((not (string-equal key (oref obj shortarg))) + (transient--add-face key 'transient-mismatched-key)) + (key)))) + +(cl-defgeneric transient-format-description (obj) + "Format OBJ's `description' for display and return the result.") + +(cl-defmethod transient-format-description ((obj transient-suffix)) + "The `description' slot may be a function, in which case that is +called inside the correct buffer (see `transient--insert-group') +and its value is returned to the caller." + (transient--get-description obj)) + +(cl-defmethod transient-format-description ((obj transient-group)) + "Format the description by calling the next method. If the result +doesn't use the `face' property at all, then apply the face +`transient-heading' to the complete string." + (and-let* ((desc (transient--get-description obj))) + (cond ((oref obj inapt) + (propertize desc 'face 'transient-inapt-suffix)) + ((text-property-not-all 0 (length desc) 'face nil desc) + desc) + ((propertize desc 'face 'transient-heading))))) + +(cl-defmethod transient-format-description :around ((obj transient-suffix)) + "Format the description by calling the next method. If the result +is nil, then use \"(BUG: no description)\" as the description. +If the OBJ's `key' is currently unreachable, then apply the face +`transient-unreachable' to the complete string." + (let ((desc (or (cl-call-next-method obj) + (and (slot-boundp transient--prefix 'suffix-description) + (funcall (oref transient--prefix suffix-description) + obj))))) + (if desc + (when-let ((face (transient--get-face obj 'face))) + (setq desc (transient--add-face desc face t))) + (setq desc (propertize "(BUG: no description)" 'face 'error))) + (when (if transient--all-levels-p + (> (oref obj level) transient--default-prefix-level) + (and transient-highlight-higher-levels + (> (max (oref obj level) transient--max-group-level) + transient--default-prefix-level))) + (setq desc (transient--add-face desc 'transient-higher-level))) + (when-let ((inapt-face (and (oref obj inapt) + (transient--get-face obj 'inapt-face)))) + (setq desc (transient--add-face desc inapt-face))) + (when (and (slot-boundp obj 'key) + (transient--key-unreachable-p obj)) + (setq desc (transient--add-face desc 'transient-unreachable))) + desc)) + +(cl-defgeneric transient-format-value (obj) + "Format OBJ's value for display and return the result.") + +(cl-defmethod transient-format-value ((obj transient-suffix)) + (propertize (oref obj argument) + 'face (if (oref obj value) + 'transient-argument + 'transient-inactive-argument))) + +(cl-defmethod transient-format-value ((obj transient-option)) + (let ((argument (oref obj argument))) + (if-let ((value (oref obj value))) + (pcase-exhaustive (oref obj multi-value) + ('nil + (concat (propertize argument 'face 'transient-argument) + (propertize value 'face 'transient-value))) + ((or 't 'rest) + (concat (propertize (if (string-suffix-p " " argument) + argument + (concat argument " ")) + 'face 'transient-argument) + (propertize (mapconcat #'prin1-to-string value " ") + 'face 'transient-value))) + ('repeat + (mapconcat (lambda (value) + (concat (propertize argument 'face 'transient-argument) + (propertize value 'face 'transient-value))) + value " "))) + (propertize argument 'face 'transient-inactive-argument)))) + +(cl-defmethod transient-format-value ((obj transient-switches)) + (with-slots (value argument-format choices) obj + (format (propertize argument-format + 'face (if value + 'transient-argument + 'transient-inactive-argument)) + (format + (propertize "[%s]" 'face 'transient-delimiter) + (mapconcat + (lambda (choice) + (propertize choice 'face + (if (equal (format argument-format choice) value) + 'transient-value + 'transient-inactive-value))) + choices + (propertize "|" 'face 'transient-delimiter)))))) + +(cl-defmethod transient--get-description ((obj transient-child)) + (and-let* ((desc (oref obj description))) + (if (functionp desc) + (if (= (car (transient--func-arity desc)) 1) + (funcall desc obj) + (funcall desc)) + desc))) + +(cl-defmethod transient--get-face ((obj transient-suffix) slot) + (and-let* (((slot-boundp obj slot)) + (face (slot-value obj slot))) + (if (and (not (facep face)) + (functionp face)) + (let ((transient--pending-suffix obj)) + (if (= (car (transient--func-arity face)) 1) + (funcall face obj) + (funcall face))) + face))) + +(defun transient--add-face (string face &optional append beg end) + (let ((str (copy-sequence string))) + (add-face-text-property (or beg 0) (or end (length str)) face append str) + str)) + +(defun transient--key-face (&optional cmd enforce-type) + (or (and transient-semantic-coloring + (not transient--helpp) + (not transient--editp) + (or (and cmd (get cmd 'transient-face)) + (get (transient--get-pre-command cmd enforce-type) + 'transient-face))) + (if cmd 'transient-key 'transient-key-noop))) + +(defun transient--key-unreachable-p (obj) + (and transient--redisplay-key + (let ((key (oref obj key))) + (not (or (equal (seq-take (cl-coerce (edmacro-parse-keys key t) 'list) + (length transient--redisplay-key)) + transient--redisplay-key) + (transient--lookup-key transient-sticky-map (kbd key))))))) + +(defun transient--lookup-key (keymap key) + (let ((val (lookup-key keymap key))) + (and val (not (integerp val)) val))) + +(defun transient--maybe-pad-keys (group &optional parent) + (when-let ((pad (or (oref group pad-keys) + (and parent (oref parent pad-keys))))) + (oset group pad-keys + (apply #'max + (if (integerp pad) pad 0) + (seq-keep (lambda (suffix) + (and (eieio-object-p suffix) + (slot-boundp suffix 'key) + (length (oref suffix key)))) + (oref group suffixes)))))) + +(defun transient--pixel-width (string) + (save-window-excursion + (with-temp-buffer + (insert string) + (set-window-dedicated-p nil nil) + (set-window-buffer nil (current-buffer)) + (car (window-text-pixel-size + nil (line-beginning-position) (point)))))) + +(defun transient-command-summary-or-name (obj) + "Return the summary or name of the command represented by OBJ. + +If the command has a doc-string, then return the first line of +that, else its name. + +Intended to be temporarily used as the `:suffix-description' of +a prefix command, while porting a regular keymap to a transient." + (let ((command (oref obj command))) + (if-let ((doc (documentation command))) + (propertize (car (split-string doc "\n")) 'face 'font-lock-doc-face) + (propertize (symbol-name command) 'face 'font-lock-function-name-face)))) + +;;; Help + +(cl-defgeneric transient-show-help (obj) + "Show documentation for the command represented by OBJ.") + +(cl-defmethod transient-show-help ((obj transient-prefix)) + "Call `show-help' if non-nil, else show `info-manual', +if non-nil, else show the `man-page' if non-nil, else use +`describe-function'." + (with-slots (show-help info-manual man-page command) obj + (cond (show-help (funcall show-help obj)) + (info-manual (transient--show-manual info-manual)) + (man-page (transient--show-manpage man-page)) + ((transient--describe-function command))))) + +(cl-defmethod transient-show-help ((obj transient-suffix)) + "Call `show-help' if non-nil, else use `describe-function'. +Also used to dispatch showing documentation for the current +prefix. If the suffix is a sub-prefix, then also call the +prefix method." + (cond + ((eq this-command 'transient-help) + (transient-show-help transient--prefix)) + ((let ((prefix (get (oref obj command) + 'transient--prefix))) + (and prefix (not (eq (oref transient--prefix command) this-command)) + (prog1 t (transient-show-help prefix))))) + ((if-let ((show-help (oref obj show-help))) + (funcall show-help obj) + (transient--describe-function this-command))))) + +(cl-defmethod transient-show-help ((obj transient-infix)) + "Call `show-help' if non-nil, else show the `man-page' +if non-nil, else use `describe-function'. When showing the +manpage, then try to jump to the correct location." + (if-let ((show-help (oref obj show-help))) + (funcall show-help obj) + (if-let ((man-page (oref transient--prefix man-page)) + (argument (and (slot-boundp obj 'argument) + (oref obj argument)))) + (transient--show-manpage man-page argument) + (transient--describe-function this-command)))) + +;; `cl-generic-generalizers' doesn't support `command' et al. +(cl-defmethod transient-show-help (cmd) + "Show the command doc-string." + (transient--describe-function cmd)) + +(defun transient--describe-function (fn) + (describe-function fn) + (unless (derived-mode-p 'help-mode) + (when-let* ((buf (get-buffer "*Help*")) + (win (or (and buf (get-buffer-window buf)) + (cl-find-if (lambda (win) + (with-current-buffer (window-buffer win) + (derived-mode-p 'help-mode))) + (window-list))))) + (select-window win)))) + +(defun transient--show-manual (manual) + (info manual)) + +(defun transient--show-manpage (manpage &optional argument) + (require 'man) + (let* ((Man-notify-method 'meek) + (buf (Man-getpage-in-background manpage)) + (proc (get-buffer-process buf))) + (while (and proc (eq (process-status proc) 'run)) + (accept-process-output proc)) + (switch-to-buffer buf) + (when argument + (transient--goto-argument-description argument)))) + +(defun transient--goto-argument-description (arg) + (goto-char (point-min)) + (let ((case-fold-search nil) + ;; This matches preceding/proceeding options. Options + ;; such as "-a", "-S[<keyid>]", and "--grep=<pattern>" + ;; are matched by this regex without the shy group. + ;; The ". " in the shy group is for options such as + ;; "-m parent-number", and the "-[^[:space:]]+ " is + ;; for options such as "--mainline parent-number" + (others "-\\(?:. \\|-[^[:space:]]+ \\)?[^[:space:]]+")) + (when (re-search-forward + (if (equal arg "--") + ;; Special case. + "^[\t\s]+\\(--\\(?: \\|$\\)\\|\\[--\\]\\)" + ;; Should start with whitespace and may have + ;; any number of options before and/or after. + (format + "^[\t\s]+\\(?:%s, \\)*?\\(?1:%s\\)%s\\(?:, %s\\)*$" + others + ;; Options don't necessarily end in an "=" + ;; (e.g., "--gpg-sign[=<keyid>]") + (string-remove-suffix "=" arg) + ;; Simple options don't end in an "=". Splitting this + ;; into 2 cases should make getting false positives + ;; less likely. + (if (string-suffix-p "=" arg) + ;; "[^[:space:]]*[^.[:space:]]" matches the option + ;; value, which is usually after the option name + ;; and either '=' or '[='. The value can't end in + ;; a period, as that means it's being used at the + ;; end of a sentence. The space is for options + ;; such as '--mainline parent-number'. + "\\(?: \\|\\[?=\\)[^[:space:]]*[^.[:space:]]" + ;; Either this doesn't match anything (e.g., "-a"), + ;; or the option is followed by a value delimited + ;; by a "[", "<", or ":". A space might appear + ;; before this value, as in "-f <file>". The + ;; space alternative is for options such as + ;; "-m parent-number". + "\\(?:\\(?: \\| ?[\\[<:]\\)[^[:space:]]*[^.[:space:]]\\)?") + others)) + nil t) + (goto-char (match-beginning 1))))) + +(defun transient--insert-help () + (unless (looking-back "\n\n" 2) + (insert "\n")) + (when transient--helpp + (insert + (format (propertize "\ +Type a %s to show help for that suffix command, or %s to show manual. +Type %s to exit help.\n" + 'face 'transient-heading) + (propertize "<KEY>" 'face 'transient-key) + (propertize "?" 'face 'transient-key) + (propertize "C-g" 'face 'transient-key)))) + (when transient--editp + (unless transient--helpp + (insert + (format (propertize "\ +Type a %s to set level for that suffix command. +Type %s to set what levels are available for this prefix command.\n" + 'face 'transient-heading) + (propertize "<KEY>" 'face 'transient-key) + (propertize "C-x l" 'face 'transient-key)))) + (with-slots (level) transient--prefix + (insert + (format (propertize " +Suffixes on levels %s are available. +Suffixes on levels %s and %s are unavailable.\n" + 'face 'transient-heading) + (propertize (format "1-%s" level) + 'face 'transient-enabled-suffix) + (propertize " 0 " + 'face 'transient-disabled-suffix) + (propertize (format ">=%s" (1+ level)) + 'face 'transient-disabled-suffix)))))) + +;;; Popup Navigation + +(defun transient-scroll-up (&optional arg) + "Scroll text of transient popup window upward ARG lines. +If ARG is nil scroll near full screen. This is a wrapper +around `scroll-up-command' (which see)." + (interactive "^P") + (with-selected-window transient--window + (scroll-up-command arg))) + +(defun transient-scroll-down (&optional arg) + "Scroll text of transient popup window down ARG lines. +If ARG is nil scroll near full screen. This is a wrapper +around `scroll-down-command' (which see)." + (interactive "^P") + (with-selected-window transient--window + (scroll-down-command arg))) + +(defun transient-backward-button (n) + "Move to the previous button in the transient popup buffer. +See `backward-button' for information about N." + (interactive "p") + (with-selected-window transient--window + (backward-button n t))) + +(defun transient-forward-button (n) + "Move to the next button in the transient popup buffer. +See `forward-button' for information about N." + (interactive "p") + (with-selected-window transient--window + (forward-button n t))) + +(define-button-type 'transient + 'face nil + 'keymap transient-button-map) + +(defun transient--goto-button (command) + (cond + ((stringp command) + (when (re-search-forward (concat "^" (regexp-quote command)) nil t) + (goto-char (match-beginning 0)))) + (command + (while (and (ignore-errors (forward-button 1)) + (not (eq (button-get (button-at (point)) 'command) command)))) + (unless (eq (button-get (button-at (point)) 'command) command) + (goto-char (point-min)) + (forward-button 1))))) + +(defun transient--heading-at-point () + (and (eq (get-text-property (point) 'face) 'transient-heading) + (let ((beg (line-beginning-position))) + (buffer-substring-no-properties + beg (next-single-property-change + beg 'face nil (line-end-position)))))) + +;;; Compatibility +;;;; Popup Isearch + +(defvar-keymap transient--isearch-mode-map + :parent isearch-mode-map + "<remap> <isearch-exit>" #'transient-isearch-exit + "<remap> <isearch-cancel>" #'transient-isearch-cancel + "<remap> <isearch-abort>" #'transient-isearch-abort) + +(defun transient-isearch-backward (&optional regexp-p) + "Do incremental search backward. +With a prefix argument, do an incremental regular expression +search instead." + (interactive "P") + (transient--isearch-setup) + (let ((isearch-mode-map transient--isearch-mode-map)) + (isearch-mode nil regexp-p))) + +(defun transient-isearch-forward (&optional regexp-p) + "Do incremental search forward. +With a prefix argument, do an incremental regular expression +search instead." + (interactive "P") + (transient--isearch-setup) + (let ((isearch-mode-map transient--isearch-mode-map)) + (isearch-mode t regexp-p))) + +(defun transient-isearch-exit () + "Like `isearch-exit' but adapted for `transient'." + (interactive) + (isearch-exit) + (transient--isearch-exit)) + +(defun transient-isearch-cancel () + "Like `isearch-cancel' but adapted for `transient'." + (interactive) + (condition-case nil (isearch-cancel) (quit)) + (transient--isearch-exit)) + +(defun transient-isearch-abort () + "Like `isearch-abort' but adapted for `transient'." + (interactive) + (let ((around (lambda (fn) + (condition-case nil (funcall fn) (quit)) + (transient--isearch-exit)))) + (advice-add 'isearch-cancel :around around) + (unwind-protect + (isearch-abort) + (advice-remove 'isearch-cancel around)))) + +(defun transient--isearch-setup () + (select-window transient--window) + (transient--suspend-override t)) + +(defun transient--isearch-exit () + (select-window transient--original-window) + (transient--resume-override)) + +;;;; Edebug + +(defun transient--edebug-command-p () + (and (bound-and-true-p edebug-active) + (or (memq this-command '(top-level abort-recursive-edit)) + (string-prefix-p "edebug" (symbol-name this-command))))) + +;;;; Miscellaneous + +(cl-pushnew (list nil (concat "^\\s-*(" + (eval-when-compile + (regexp-opt + '("transient-define-prefix" + "transient-define-suffix" + "transient-define-infix" + "transient-define-argument") + t)) + "\\s-+\\(" lisp-mode-symbol-regexp "\\)") + 2) + lisp-imenu-generic-expression :test #'equal) + +(declare-function which-key-mode "which-key" (&optional arg)) + +(defun transient--suspend-which-key-mode () + (when (bound-and-true-p which-key-mode) + (which-key-mode -1) + (add-hook 'transient-exit-hook #'transient--resume-which-key-mode))) + +(defun transient--resume-which-key-mode () + (unless transient--prefix + (which-key-mode 1) + (remove-hook 'transient-exit-hook #'transient--resume-which-key-mode))) + +(defun transient-bind-q-to-quit () + "Modify some keymaps to bind \"q\" to the appropriate quit command. + +\"C-g\" is the default binding for such commands now, but Transient's +predecessor Magit-Popup used \"q\" instead. If you would like to get +that binding back, then call this function in your init file like so: + + (with-eval-after-load \\='transient + (transient-bind-q-to-quit)) + +Individual transients may already bind \"q\" to something else +and such a binding would shadow the quit binding. If that is the +case then \"Q\" is bound to whatever \"q\" would have been bound +to by setting `transient-substitute-key-function' to a function +that does that. Of course \"Q\" may already be bound to something +else, so that function binds \"M-q\" to that command instead. +Of course \"M-q\" may already be bound to something else, but +we stop there." + (keymap-set transient-base-map "q" #'transient-quit-one) + (keymap-set transient-sticky-map "q" #'transient-quit-seq) + (setq transient-substitute-key-function + #'transient-rebind-quit-commands)) + +(defun transient-rebind-quit-commands (obj) + "See `transient-bind-q-to-quit'." + (let ((key (oref obj key))) + (cond ((string-equal key "q") "Q") + ((string-equal key "Q") "M-q") + (key)))) + +(defun transient--force-fixed-pitch () + (require 'face-remap) + (face-remap-reset-base 'default) + (face-remap-add-relative 'default 'fixed-pitch)) + +(defun transient--func-arity (fn) + (func-arity (advice--cd*r (if (symbolp fn) (symbol-function fn) fn)))) + +(defun transient--seq-reductions-from (function sequence initial-value) + (let ((acc (list initial-value))) + (seq-doseq (elt sequence) + (push (funcall function (car acc) elt) acc)) + (nreverse acc))) + +;;; Font-Lock + +(defconst transient-font-lock-keywords + (eval-when-compile + `((,(concat "(" + (regexp-opt (list "transient-define-prefix" + "transient-define-infix" + "transient-define-argument" + "transient-define-suffix") + t) + "\\_>[ \t'(]*" + "\\(\\(?:\\sw\\|\\s_\\)+\\)?") + (1 'font-lock-keyword-face) + (2 'font-lock-function-name-face nil t))))) + +(font-lock-add-keywords 'emacs-lisp-mode transient-font-lock-keywords) + +;;; Auxiliary Classes +;;;; `transient-lisp-variable' + +(defclass transient-lisp-variable (transient-variable) + ((reader :initform #'transient-lisp-variable--reader) + (always-read :initform t) + (set-value :initarg :set-value :initform #'set)) + "[Experimental] Class used for Lisp variables.") + +(cl-defmethod transient-init-value ((obj transient-lisp-variable)) + (oset obj value (symbol-value (oref obj variable)))) + +(cl-defmethod transient-infix-set ((obj transient-lisp-variable) value) + (funcall (oref obj set-value) + (oref obj variable) + (oset obj value value))) + +(cl-defmethod transient-format-description ((obj transient-lisp-variable)) + (or (cl-call-next-method obj) + (symbol-name (oref obj variable)))) + +(cl-defmethod transient-format-value ((obj transient-lisp-variable)) + (propertize (prin1-to-string (oref obj value)) + 'face 'transient-value)) + +(cl-defmethod transient-prompt ((obj transient-lisp-variable)) + (if (and (slot-boundp obj 'prompt) + (oref obj prompt)) + (cl-call-next-method obj) + (format "Set %s: " (oref obj variable)))) + +(defun transient-lisp-variable--reader (prompt initial-input _history) + (read--expression prompt initial-input)) + +;;; _ +(provide 'transient) +;; Local Variables: +;; indent-tabs-mode: nil +;; checkdoc-symbol-words: ("command-line" "edit-mode" "help-mode") +;; End: +;;; transient.el ends here diff --git a/emacs/elpa/transient-20240607.1832/transient.elc b/emacs/elpa/transient-20240607.1832/transient.elc Binary files differ. diff --git a/emacs/elpa/transient-20240607.1832/transient.info b/emacs/elpa/transient-20240607.1832/transient.info @@ -0,0 +1,3273 @@ +This is transient.info, produced by makeinfo version 6.7 from +transient.texi. + + Copyright (C) 2018–2024 Free Software Foundation, Inc. + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +INFO-DIR-SECTION Emacs misc features +START-INFO-DIR-ENTRY +* Transient: (transient). Transient Commands. +END-INFO-DIR-ENTRY + + +File: transient.info, Node: Top, Next: Introduction, Up: (dir) + +Transient User and Developer Manual +*********************************** + +Transient is the library used to implement the keyboard-driven “menus” +in Magit. It is distributed as a separate package, so that it can be +used to implement similar menus in other packages. + + This manual can be bit hard to digest when getting started. A useful +resource to get over that hurdle is Psionic K’s interactive tutorial, +available at <https://github.com/positron-solutions/transient-showcase>. + +This manual is for Transient version 0.6.0. + + Copyright (C) 2018–2024 Free Software Foundation, Inc. + + You can redistribute this document and/or modify it under the terms + of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) + any later version. + + This document is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +* Menu: + +* Introduction:: +* Usage:: +* Modifying Existing Transients:: +* Defining New Commands:: +* Classes and Methods:: +* FAQ:: +* Keystroke Index:: +* Command and Function Index:: +* Variable Index:: +* Concept Index:: +* GNU General Public License:: + +— The Detailed Node Listing — + +Usage + +* Invoking Transients:: +* Aborting and Resuming Transients:: +* Common Suffix Commands:: +* Saving Values:: +* Using History:: +* Getting Help for Suffix Commands:: +* Enabling and Disabling Suffixes:: +* Other Commands:: +* Configuration:: + +Defining New Commands + +* Technical Introduction:: +* Defining Transients:: +* Binding Suffix and Infix Commands:: +* Defining Suffix and Infix Commands:: +* Using Infix Arguments:: +* Transient State:: + +Binding Suffix and Infix Commands + +* Group Specifications:: +* Suffix Specifications:: + + +Classes and Methods + +* Group Classes:: +* Group Methods:: +* Prefix Classes:: +* Suffix Classes:: +* Suffix Methods:: +* Prefix Slots:: +* Suffix Slots:: +* Predicate Slots:: + +Suffix Methods + +* Suffix Value Methods:: +* Suffix Format Methods:: + + + + +File: transient.info, Node: Introduction, Next: Usage, Prev: Top, Up: Top + +1 Introduction +************** + +Transient is the library used to implement the keyboard-driven “menus” +in Magit. It is distributed as a separate package, so that it can be +used to implement similar menus in other packages. + + This manual can be bit hard to digest when getting started. A useful +resource to get over that hurdle is Psionic K’s interactive tutorial, +available at <https://github.com/positron-solutions/transient-showcase>. + +Some things that Transient can do +================================= + + • Display current state of arguments + • Display and manage lifecycle of modal bindings + • Contextual user interface + • Flow control for wizard-like composition of interactive forms + • History & persistence + • Rendering arguments for controlling CLI programs + +Complexity in CLI programs +========================== + +Complexity tends to grow with time. How do you manage the complexity of +commands? Consider the humble shell command ‘ls’. It now has over +_fifty_ command line options. Some of these are boolean flags (‘ls +-l’). Some take arguments (‘ls --sort=s’). Some have no effect unless +paired with other flags (‘ls -lh’). Some are mutually exclusive. Some +shell commands even have so many options that they introduce +_subcommands_ (‘git branch’, ‘git commit’), each with their own rich set +of options (‘git branch -f’). + +Using Transient for composing interactive commands +================================================== + +What about Emacs commands used interactively? How do these handle +options? One solution is to make many versions of the same command, so +you don’t need to! Consider: ‘delete-other-windows’ vs. +‘delete-other-windows-vertically’ (among many similar examples). + + Some Emacs commands will simply prompt you for the next "argument" +(‘M-x switch-to-buffer’). Another common solution is to use prefix +arguments which usually start with ‘C-u’. Sometimes these are sensibly +numerical in nature (‘C-u 4 M-x forward-paragraph’ to move forward 4 +paragraphs). But sometimes they function instead as boolean "switches" +(‘C-u C-SPACE’ to jump to the last mark instead of just setting it, ‘C-u +C-u C-SPACE’ to unconditionally set the mark). Since there aren’t many +standards for the use of prefix options, you have to read the command’s +documentation to find out what the possibilities are. + + But when an Emacs command grows to have a truly large set of options +and arguments, with dependencies between them, lots of option values, +etc., these simple approaches just don’t scale. Transient is designed +to solve this issue. Think of it as the humble prefix argument ‘C-u’, +_raised to the power of 10_. Like ‘C-u’, it is key driven. Like the +shell, it supports boolean "flag" options, options that take arguments, +and even "sub-commands", with their own options. But instead of +searching through a man page or command documentation, well-designed +transients _guide_ their users to the relevant set of options (and even +their possible values!) directly, taking into account any important +pre-existing Emacs settings. And while for shell commands like ‘ls’, +there is only one way to "execute" (hit ‘Return’!), transients can +"execute" using multiple different keys tied to one of many +self-documenting _actions_ (imagine having 5 different colored return +keys on your keyboard!). Transients make navigating and setting large, +complex groups of command options and arguments easy. Fun even. Once +you’ve tried it, it’s hard to go back to the ‘C-u what can I do here +again?’ way. + + +File: transient.info, Node: Usage, Next: Modifying Existing Transients, Prev: Introduction, Up: Top + +2 Usage +******* + +* Menu: + +* Invoking Transients:: +* Aborting and Resuming Transients:: +* Common Suffix Commands:: +* Saving Values:: +* Using History:: +* Getting Help for Suffix Commands:: +* Enabling and Disabling Suffixes:: +* Other Commands:: +* Configuration:: + + +File: transient.info, Node: Invoking Transients, Next: Aborting and Resuming Transients, Up: Usage + +2.1 Invoking Transients +======================= + +A transient prefix command is invoked like any other command by pressing +the key that is bound to that command. The main difference to other +commands is that a transient prefix command activates a transient +keymap, which temporarily binds the transient’s infix and suffix +commands. Bindings from other keymaps may, or may not, be disabled +while the transient state is in effect. + + There are two kinds of commands that are available after invoking a +transient prefix command; infix and suffix commands. Infix commands set +some value (which is then shown in a popup buffer), without leaving the +transient. Suffix commands, on the other hand, usually quit the +transient and they may use the values set by the infix commands, i.e., +the infix *arguments*. + + Instead of setting arguments to be used by a suffix command, infix +commands may also set some value by side-effect, e.g., by setting the +value of some variable. + + +File: transient.info, Node: Aborting and Resuming Transients, Next: Common Suffix Commands, Prev: Invoking Transients, Up: Usage + +2.2 Aborting and Resuming Transients +==================================== + +To quit the transient without invoking a suffix command press ‘C-g’. + + Key bindings in transient keymaps may be longer than a single event. +After pressing a valid prefix key, all commands whose bindings do not +begin with that prefix key are temporarily unavailable and grayed out. +To abort the prefix key press ‘C-g’ (which in this case only quits the +prefix key, but not the complete transient). + + A transient prefix command can be bound as a suffix of another +transient. Invoking such a suffix replaces the current transient state +with a new transient state, i.e., the available bindings change and the +information displayed in the popup buffer is updated accordingly. +Pressing ‘C-g’ while a nested transient is active only quits the +innermost transient, causing a return to the previous transient. + + ‘C-q’ or ‘C-z’ on the other hand always exits all transients. If you +use the latter, then you can later resume the stack of transients using +‘M-x transient-resume’. + +‘C-g’ (‘transient-quit-seq’) +‘C-g’ (‘transient-quit-one’) + This key quits the currently active incomplete key sequence, if + any, or else the current transient. When quitting the current + transient, it returns to the previous transient, if any. + + Transient’s predecessor bound ‘q’ instead of ‘C-g’ to the quit +command. To learn how to get that binding back see +‘transient-bind-q-to-quit’’s documentation string. + +‘C-q’ (‘transient-quit-all’) + This command quits the currently active incomplete key sequence, if + any, and all transients, including the active transient and all + suspended transients, if any. + +‘C-z’ (‘transient-suspend’) + Like ‘transient-quit-all’, this command quits an incomplete key + sequence, if any, and all transients. Additionally, it saves the + stack of transients so that it can easily be resumed (which is + particularly useful if you quickly need to do “something else” and + the stack is deeper than a single transient, and/or you have + already changed the values of some infix arguments). + + Note that only a single stack of transients can be saved at a time. + If another stack is already saved, then saving a new stack discards + the previous stack. + +‘M-x transient-resume’ + This command resumes the previously suspended stack of transients, + if any. + + +File: transient.info, Node: Common Suffix Commands, Next: Saving Values, Prev: Aborting and Resuming Transients, Up: Usage + +2.3 Common Suffix Commands +========================== + +A few shared suffix commands are available in all transients. These +suffix commands are not shown in the popup buffer by default. + + This includes the aborting commands mentioned in the previous +section, as well as some other commands that are all bound to ‘C-x KEY’. +After ‘C-x’ is pressed, a section featuring all these common commands is +temporarily shown in the popup buffer. After invoking one of them, the +section disappears again. Note, however, that one of these commands is +described as “Show common permanently”; invoke that if you want the +common commands to always be shown for all transients. + +‘C-x t’ (‘transient-toggle-common’) + This command toggles whether the generic commands that are common + to all transients are always displayed or only after typing the + incomplete prefix key sequence ‘C-x’. This only affects the + current Emacs session. + + -- User Option: transient-show-common-commands + This option controls whether shared suffix commands are shown + alongside the transient-specific infix and suffix commands. By + default, the shared commands are not shown to avoid overwhelming + the user with too many options. + + While a transient is active, pressing ‘C-x’ always shows the common + commands. The value of this option can be changed for the current + Emacs session by typing ‘C-x t’ while a transient is active. + + The other common commands are described in either the previous or in +one of the following sections. + + Some of Transient’s key bindings differ from the respective bindings +of Magit-Popup; see *note FAQ:: for more information. + + +File: transient.info, Node: Saving Values, Next: Using History, Prev: Common Suffix Commands, Up: Usage + +2.4 Saving Values +================= + +After setting the infix arguments in a transient, the user can save +those arguments for future invocations. + + Most transients will start out with the saved arguments when they are +invoked. There are a few exceptions, though. Some transients are +designed so that the value that they use is stored externally as the +buffer-local value of some variable. Invoking such a transient again +uses the buffer-local value. (1) + + If the user does not save the value and just exits using a regular +suffix command, then the value is merely saved to the transient’s +history. That value won’t be used when the transient is next invoked, +but it is easily accessible (see *note Using History::). + +‘C-x s’ (‘transient-set’) + This command saves the value of the active transient for this Emacs + session. + +‘C-x C-s’ (‘transient-save’) + Save the value of the active transient persistently across Emacs + sessions. + +‘C-x C-k’ (‘transient-reset’) + Clear the set and saved values of the active transient. + + -- User Option: transient-values-file + This option names the file that is used to persist the values of + transients between Emacs sessions. + + ---------- Footnotes ---------- + + (1) ‘magit-diff’ and ‘magit-log’ are two prominent examples, and +their handling of buffer-local values is actually a bit more complicated +than outlined above and even customizable. + + +File: transient.info, Node: Using History, Next: Getting Help for Suffix Commands, Prev: Saving Values, Up: Usage + +2.5 Using History +================= + +Every time the user invokes a suffix command the transient’s current +value is saved to its history. These values can be cycled through the +same way one can cycle through the history of commands that read +user-input in the minibuffer. + +‘C-M-p’ (‘transient-history-prev’) +‘C-x p’ + This command switches to the previous value used for the active + transient. + +‘C-M-n’ (‘transient-history-next’) +‘C-x n’ + This command switches to the next value used for the active + transient. + + In addition to the transient-wide history, Transient of course +supports per-infix history. When an infix reads user-input using the +minibuffer, the user can use the regular minibuffer history commands to +cycle through previously used values. Usually the same keys as those +mentioned above are bound to those commands. + + Authors of transients should arrange for different infix commands +that read the same kind of value to also use the same history key (see +*note Suffix Slots::). + + Both kinds of history are saved to a file when Emacs is exited. + + -- User Option: transient-history-file + This option names the file that is used to persist the history of + transients and their infixes between Emacs sessions. + + -- User Option: transient-history-limit + This option controls how many history elements are kept at the time + the history is saved in ‘transient-history-file’. + + +File: transient.info, Node: Getting Help for Suffix Commands, Next: Enabling and Disabling Suffixes, Prev: Using History, Up: Usage + +2.6 Getting Help for Suffix Commands +==================================== + +Transients can have many suffixes and infixes that the user might not be +familiar with. To make it trivial to get help for these, Transient +provides access to the documentation directly from the active transient. + +‘C-h’ (‘transient-help’) + This command enters help mode. When help mode is active, typing a + key shows information about the suffix command that the key + normally is bound to (instead of invoking it). Pressing ‘C-h’ a + second time shows information about the _prefix_ command. + + After typing a key, the stack of transient states is suspended and + information about the suffix command is shown instead. Typing ‘q’ + in the help buffer buries that buffer and resumes the transient + state. + + What sort of documentation is shown depends on how the transient was +defined. For infix commands that represent command-line arguments this +ideally shows the appropriate manpage. ‘transient-help’ then tries to +jump to the correct location within that. Info manuals are also +supported. The fallback is to show the command’s documentation string, +for non-infix suffixes this is usually appropriate. + + +File: transient.info, Node: Enabling and Disabling Suffixes, Next: Other Commands, Prev: Getting Help for Suffix Commands, Up: Usage + +2.7 Enabling and Disabling Suffixes +=================================== + +The user base of a package that uses transients can be very diverse. +This is certainly the case for Magit; some users have been using it and +Git for a decade, while others are just getting started now. + + For that reason a mechanism is needed that authors can use to +classify a transient’s infixes and suffixes along the +essentials...everything spectrum. We use the term “levels” to describe +that mechanism. + + Each suffix command is placed on a level and each transient has a +level (called “transient-level”), which controls which suffix commands +are available. Integers between 1 and 7 (inclusive) are valid levels. +For suffixes, 0 is also valid; it means that the suffix is not displayed +at any level. + + The levels of individual transients and/or their individual suffixes +can be changed interactively, by invoking the transient and then +pressing ‘C-x l’ to enter the “edit” mode, see below. + + The default level for both transients and their suffixes is 4. The +‘transient-default-level’ option only controls the default for +transients. The default suffix level is always 4. The authors of +transients should place certain suffixes on a higher level, if they +expect that it won’t be of use to most users, and they should place very +important suffixes on a lower level, so that they remain available even +if the user lowers the transient level. + + -- User Option: transient-default-level + This option controls which suffix levels are made available by + default. It sets the transient-level for transients for which the + user has not set that individually. + + -- User Option: transient-levels-file + This option names the file that is used to persist the levels of + transients and their suffixes between Emacs sessions. + +‘C-x l’ (‘transient-set-level’) + This command enters edit mode. When edit mode is active, then all + infixes and suffixes that are currently usable are displayed along + with their levels. The colors of the levels indicate whether they + are enabled or not. The level of the transient is also displayed + along with some usage information. + + In edit mode, pressing the key that would usually invoke a certain + suffix instead prompts the user for the level that suffix should be + placed on. + + Help mode is available in edit mode. + + To change the transient level press ‘C-x l’ again. + + To exit edit mode press ‘C-g’. + + Note that edit mode does not display any suffixes that are not + currently usable. ‘magit-rebase’, for example, shows different + suffixes depending on whether a rebase is already in progress or + not. The predicates also apply in edit mode. + + Therefore, to control which suffixes are available given a certain + state, you have to make sure that that state is currently active. + +‘C-x a’ (‘transient-toggle-level-limit’) + This command toggle whether suffixes that are on levels higher than + the level specified by ‘transient-default-level’ are temporarily + available anyway. + + +File: transient.info, Node: Other Commands, Next: Configuration, Prev: Enabling and Disabling Suffixes, Up: Usage + +2.8 Other Commands +================== + +When invoking a transient in a small frame, the transient window may not +show the complete buffer, making it necessary to scroll, using the +following commands. These commands are never shown in the transient +window, and the key bindings are the same as for ‘scroll-up-command’ and +‘scroll-down-command’ in other buffers. + + -- Command: transient-scroll-up arg + This command scrolls text of transient popup window upward ARG + lines. If ARG is ‘nil’, then it scrolls near full screen. This is + a wrapper around ‘scroll-up-command’ (which see). + + -- Command: transient-scroll-down arg + This command scrolls text of transient popup window down ARG lines. + If ARG is ‘nil’, then it scrolls near full screen. This is a + wrapper around ‘scroll-down-command’ (which see). + + +File: transient.info, Node: Configuration, Prev: Other Commands, Up: Usage + +2.9 Configuration +================= + +More options are described in *note Common Suffix Commands::, in *note +Saving Values::, in *note Using History:: and in *note Enabling and +Disabling Suffixes::. + +Essential Options +----------------- + +Also see *note Common Suffix Commands::. + + -- User Option: transient-show-popup + This option controls whether the current transient’s infix and + suffix commands are shown in the popup buffer. + + • If ‘t’ (the default) then the popup buffer is shown as soon as + a transient prefix command is invoked. + + • If ‘nil’, then the popup buffer is not shown unless the user + explicitly requests it, by pressing an incomplete prefix key + sequence. + + • If a number, then the a brief one-line summary is shown + instead of the popup buffer. If zero or negative, then not + even that summary is shown; only the pressed key itself is + shown. + + The popup is shown when the user explicitly requests it by + pressing an incomplete prefix key sequence. Unless this is + zero, the popup is shown after that many seconds of inactivity + (using the absolute value). + + -- User Option: transient-enable-popup-navigation + This option controls whether navigation commands are enabled in the + transient popup buffer. + + While a transient is active the transient popup buffer is not the + current buffer, making it necessary to use dedicated commands to + act on that buffer itself. This is disabled by default. If this + option is non-‘nil’, then the following features are available: + + • ‘<UP>’ moves the cursor to the previous suffix. + • ‘<DOWN>’ moves the cursor to the next suffix. + • ‘<RET>’ invokes the suffix the cursor is on. + • ‘mouse-1’ invokes the clicked on suffix. + • ‘C-s’ and ‘C-r’ start isearch in the popup buffer. + + -- User Option: transient-display-buffer-action + This option specifies the action used to display the transient + popup buffer. The transient popup buffer is displayed in a window + using ‘(display-buffer BUFFER transient-display-buffer-action)’. + + The value of this option has the form ‘(FUNCTION . ALIST)’, where + FUNCTION is a function or a list of functions. Each such function + should accept two arguments: a buffer to display and an alist of + the same form as ALIST. See *note (elisp)Choosing Window::, for + details. + + The default is: + + (display-buffer-in-side-window + (side . bottom) + (inhibit-same-window . t) + (window-parameters (no-other-window . t))) + + This displays the window at the bottom of the selected frame. + Another useful FUNCTION is ‘display-buffer-below-selected’, which + is what ‘magit-popup’ used by default. For more alternatives see + *note (elisp)Buffer Display Action Functions::, and *note + (elisp)Buffer Display Action Alists::. + + Note that the buffer that was current before the transient buffer + is shown should remain the current buffer. Many suffix commands + act on the thing at point, if appropriate, and if the transient + buffer became the current buffer, then that would change what is at + point. To that effect ‘inhibit-same-window’ ensures that the + selected window is not used to show the transient buffer. + + It may be possible to display the window in another frame, but + whether that works in practice depends on the window-manager. If + the window manager selects the new window (Emacs frame), then that + unfortunately changes which buffer is current. + + If you change the value of this option, then you might also want to + change the value of ‘transient-mode-line-format’. + +Accessibility Options +--------------------- + + -- User Option: transient-force-single-column + This option controls whether the use of a single column to display + suffixes is enforced. This might be useful for users with low + vision who use large text and might otherwise have to scroll in two + dimensions. + +Auxiliary Options +----------------- + + -- User Option: transient-mode-line-format + This option controls whether the transient popup buffer has a + mode-line, separator line, or neither. + + If ‘nil’, then the buffer has no mode-line. If the buffer is not + displayed right above the echo area, then this probably is not a + good value. + + If ‘line’ (the default) or a natural number, then the buffer has no + mode-line, but a line is drawn is drawn in its place. If a number + is used, that specifies the thickness of the line. On termcap + frames we cannot draw lines, so there ‘line’ and numbers are + synonyms for ‘nil’. + + The color of the line is used to indicate if non-suffixes are + allowed and whether they exit the transient. The foreground color + of ‘transient-key-noop’ (if non-suffix are disallowed), + ‘transient-key-stay’ (if allowed and transient stays active), or + ‘transient-key-exit’ (if allowed and they exit the transient) is + used to draw the line. + + Otherwise this can be any mode-line format. See *note (elisp)Mode + Line Format::, for details. + + -- User Option: transient-semantic-coloring + This option controls whether colors are used to indicate the + transient behavior of commands. + + If non-‘nil’, then the key binding of each suffix is colorized to + indicate whether it exits the transient state or not. The color of + the prefix is indicated using the line that is drawn when the value + of ‘transient-mode-line-format’ is ‘line’. + + -- User Option: transient-highlight-mismatched-keys + This option controls whether key bindings of infix commands that do + not match the respective command-line argument should be + highlighted. For other infix commands this option has no effect. + + When this option is non-‘nil’, the key binding for an infix + argument is highlighted when only a long argument (e.g., + ‘--verbose’) is specified but no shorthand (e.g., ‘-v’). In the + rare case that a shorthand is specified but the key binding does + not match, then it is highlighted differently. + + Highlighting mismatched key bindings is useful when learning the + arguments of the underlying command-line tool; you wouldn’t want to + learn any short-hands that do not actually exist. + + The highlighting is done using one of the faces + ‘transient-mismatched-key’ and ‘transient-nonstandard-key’. + + -- User Option: transient-substitute-key-function + This function is used to modify key bindings. If the value of this + option is ‘nil’ (the default), then no substitution is performed. + + This function is called with one argument, the prefix object, and + must return a key binding description, either the existing key + description it finds in the ‘key’ slot, or the key description that + replaces the prefix key. It could be used to make other + substitutions, but that is discouraged. + + For example, ‘=’ is hard to reach using my custom keyboard layout, + so I substitute ‘(’ for that, which is easy to reach using a layout + optimized for lisp. + + (setq transient-substitute-key-function + (lambda (obj) + (let ((key (oref obj key))) + (if (string-match "\\`\\(=\\)[a-zA-Z]" key) + (replace-match "(" t t key 1) + key)))) + + -- User Option: transient-read-with-initial-input + This option controls whether the last history element is used as + the initial minibuffer input when reading the value of an infix + argument from the user. If ‘nil’, there is no initial input and + the first element has to be accessed the same way as the older + elements. + + -- User Option: transient-hide-during-minibuffer-read + This option controls whether the transient buffer is hidden while + user input is being read in the minibuffer. + + -- User Option: transient-align-variable-pitch + This option controls whether columns are aligned pixel-wise in the + popup buffer. + + If this is non-‘nil’, then columns are aligned pixel-wise to + support variable-pitch fonts. Keys are not aligned, so you should + use a fixed-pitch font for the ‘transient-key’ face. Other key + faces inherit from that face unless a theme is used that breaks + that relationship. + + This option is intended for users who use a variable-pitch font for + the ‘default’ face. + + -- User Option: transient-force-fixed-pitch + This option controls whether to force the use of a monospaced font + in popup buffer. Even if you use a proportional font for the + ‘default’ face, you might still want to use a monospaced font in + transient’s popup buffer. Setting this option to ‘t’ causes + ‘default’ to be remapped to ‘fixed-pitch’ in that buffer. + +Developer Options +----------------- + +These options are mainly intended for developers. + + -- User Option: transient-detect-key-conflicts + This option controls whether key binding conflicts should be + detected at the time the transient is invoked. If so, this results + in an error, which prevents the transient from being used. Because + of that, conflicts are ignored by default. + + Conflicts cannot be determined earlier, i.e., when the transient is + being defined and when new suffixes are being added, because at + that time there can be false-positives. It is actually valid for + multiple suffixes to share a common key binding, provided the + predicates of those suffixes prevent that more than one of them is + enabled at a time. + + -- User Option: transient-highlight-higher-levels + This option controls whether suffixes that would not be available + by default are highlighted. + + When non-‘nil’ then the descriptions of suffixes are highlighted if + their level is above 4, the default of ‘transient-default-level’. + Assuming you have set that variable to 7, this highlights all + suffixes that won’t be available to users without them making the + same customization. + + +File: transient.info, Node: Modifying Existing Transients, Next: Defining New Commands, Prev: Usage, Up: Top + +3 Modifying Existing Transients +******************************* + +To an extent, transients can be customized interactively, see *note +Enabling and Disabling Suffixes::. This section explains how existing +transients can be further modified non-interactively. Let’s begin with +an example: + + (transient-append-suffix 'magit-patch-apply "-3" + '("-R" "Apply in reverse" "--reverse")) + + This inserts a new infix argument to toggle the ‘--reverse’ argument +after the infix argument that toggles ‘-3’ in ‘magit-patch-apply’. + + The following functions share a few arguments: + + • PREFIX is a transient prefix command, a symbol. + + • SUFFIX is a transient infix or suffix specification in the same + form as expected by ‘transient-define-prefix’. Note that an infix + is a special kind of suffix. Depending on context “suffixes” means + “suffixes (including infixes)” or “non-infix suffixes”. Here it + means the former. See *note Suffix Specifications::. + + SUFFIX may also be a group in the same form as expected by + ‘transient-define-prefix’. See *note Group Specifications::. + + • LOC is a command, a key vector, a key description (a string as + returned by ‘key-description’), or a list specifying coordinates + (the last element may also be a command or key). For example ‘(1 0 + -1)’ identifies the last suffix (‘-1’) of the first subgroup (‘0’) + of the second group (‘1’). + + If LOC is a list of coordinates, then it can be used to identify a + group, not just an individual suffix command. + + The function ‘transient-get-suffix’ can be useful to determine + whether a certain coordination list identifies the suffix or group + that you expect it to identify. In hairy cases it may be necessary + to look at the definition of the transient prefix command. + + These functions operate on the information stored in the +‘transient--layout’ property of the PREFIX symbol. Suffix entries in +that tree are not objects but have the form ‘(LEVEL CLASS PLIST)’, where +PLIST should set at least ‘:key’, ‘:description’ and ‘:command’. + + -- Function: transient-insert-suffix prefix loc suffix &optional + keep-other + -- Function: transient-append-suffix prefix loc suffix &optional + keep-other + These functions insert the suffix or group SUFFIX into PREFIX + before or after LOC. + + Conceptually adding a binding to a transient prefix is similar to + adding a binding to a keymap, but this is complicated by the fact + that multiple suffix commands can be bound to the same key, + provided they are never active at the same time, see *note + Predicate Slots::. + + Unfortunately both false-positives and false-negatives are + possible. To deal with the former use non-‘nil’ KEEP-OTHER. To + deal with the latter remove the conflicting binding explicitly. + + -- Function: transient-replace-suffix prefix loc suffix + This function replaces the suffix or group at LOC in PREFIX with + suffix or group SUFFIX. + + -- Function: transient-remove-suffix prefix loc + This function removes the suffix or group at LOC in PREFIX. + + -- Function: transient-get-suffix prefix loc + This function returns the suffix or group at LOC in PREFIX. The + returned value has the form mentioned above. + + -- Function: transient-suffix-put prefix loc prop value + This function edits the suffix or group at LOC in PREFIX, by + setting the PROP of its plist to VALUE. + + Most of these functions do not signal an error if they cannot perform +the requested modification. The functions that insert new suffixes show +a warning if LOC cannot be found in PREFIX without signaling an error. +The reason for doing it like this is that establishing a key binding +(and that is what we essentially are trying to do here) should not +prevent the rest of the configuration from loading. Among these +functions only ‘transient-get-suffix’ and ‘transient-suffix-put’ may +signal an error. + + +File: transient.info, Node: Defining New Commands, Next: Classes and Methods, Prev: Modifying Existing Transients, Up: Top + +4 Defining New Commands +*********************** + +* Menu: + +* Technical Introduction:: +* Defining Transients:: +* Binding Suffix and Infix Commands:: +* Defining Suffix and Infix Commands:: +* Using Infix Arguments:: +* Transient State:: + + +File: transient.info, Node: Technical Introduction, Next: Defining Transients, Up: Defining New Commands + +4.1 Technical Introduction +========================== + +Taking inspiration from prefix keys and prefix arguments, Transient +implements a similar abstraction involving a prefix command, infix +arguments and suffix commands. + + When the user calls a transient prefix command, a transient +(temporary) keymap is activated, which binds the transient’s infix and +suffix commands, and functions that control the transient state are +added to ‘pre-command-hook’ and ‘post-command-hook’. The available +suffix and infix commands and their state are shown in a popup buffer +until the transient state is exited by invoking a suffix command. + + Calling an infix command causes its value to be changed. How that is +done depends on the type of the infix command. The simplest case is an +infix command that represents a command-line argument that does not take +a value. Invoking such an infix command causes the switch to be toggled +on or off. More complex infix commands may read a value from the user, +using the minibuffer. + + Calling a suffix command usually causes the transient to be exited; +the transient keymaps and hook functions are removed, the popup buffer +no longer shows information about the (no longer bound) suffix commands, +the values of some public global variables are set, while some internal +global variables are unset, and finally the command is actually called. +Suffix commands can also be configured to not exit the transient. + + A suffix command can, but does not have to, use the infix arguments +in much the same way any command can choose to use or ignore the prefix +arguments. For a suffix command that was invoked from a transient, the +variable ‘transient-current-suffixes’ and the function ‘transient-args’ +serve about the same purpose as the variables ‘prefix-arg’ and +‘current-prefix-arg’ do for any command that was called after the prefix +arguments have been set using a command such as ‘universal-argument’. + + Transient can be used to implement simple “command dispatchers”. The +main benefit then is that the user can see all the available commands in +a popup buffer, which can be thought of as a “menus”. That is useful by +itself because it frees the user from having to remember all the keys +that are valid after a certain prefix key or command. Magit’s +‘magit-dispatch’ (on ‘C-x M-g’) command is an example of using Transient +to merely implement a command dispatcher. + + In addition to that, Transient also allows users to interactively +pass arguments to commands. These arguments can be much more complex +than what is reasonable when using prefix arguments. There is a limit +to how many aspects of a command can be controlled using prefix +arguments. Furthermore, what a certain prefix argument means for +different commands can be completely different, and users have to read +documentation to learn and then commit to memory what a certain prefix +argument means to a certain command. + + Transient suffix commands, on the other hand, can accept dozens of +different arguments without the user having to remember anything. When +using Transient, one can call a command with arguments that are just as +complex as when calling the same function non-interactively from Lisp. + + Invoking a transient suffix command with arguments is similar to +invoking a command in a shell with command-line completion and history +enabled. One benefit of the Transient interface is that it remembers +history not only on a global level (“this command was invoked using +these arguments, and previously it was invoked using those other +arguments”), but also remembers the values of individual arguments +independently. See *note Using History::. + + After a transient prefix command is invoked, ‘C-h KEY’ can be used to +show the documentation for the infix or suffix command that ‘KEY’ is +bound to (see *note Getting Help for Suffix Commands::), and infixes and +suffixes can be removed from the transient using ‘C-x l KEY’. Infixes +and suffixes that are disabled by default can be enabled the same way. +See *note Enabling and Disabling Suffixes::. + + Transient ships with support for a few different types of specialized +infix commands. A command that sets a command line option, for example, +has different needs than a command that merely toggles a boolean flag. +Additionally, Transient provides abstractions for defining new types, +which the author of Transient did not anticipate (or didn’t get around +to implementing yet). + + Note that suffix commands also support regular prefix arguments. A +suffix command may even be called with both infix and prefix arguments +at the same time. If you invoke a command as a suffix of a transient +prefix command, but also want to pass prefix arguments to it, then first +invoke the prefix command, and only after doing that invoke the prefix +arguments, before finally invoking the suffix command. If you instead +began by providing the prefix arguments, then those would apply to the +prefix command, not the suffix command. Likewise, if you want to change +infix arguments before invoking a suffix command with prefix arguments, +then change the infix arguments before invoking the prefix arguments. +In other words, regular prefix arguments always apply to the next +command, and since transient prefix, infix and suffix commands are just +regular commands, the same applies to them. (Regular prefix keys behave +differently because they are not commands at all, instead they are just +incomplete key sequences, and those cannot be interrupted with prefix +commands.) + + +File: transient.info, Node: Defining Transients, Next: Binding Suffix and Infix Commands, Prev: Technical Introduction, Up: Defining New Commands + +4.2 Defining Transients +======================= + +A transient consists of a prefix command and at least one suffix +command, though usually a transient has several infix and suffix +commands. The below macro defines the transient prefix command *and* +binds the transient’s infix and suffix commands. In other words, it +defines the complete transient, not just the transient prefix command +that is used to invoke that transient. + + -- Macro: transient-define-prefix name arglist [docstring] [keyword + value]... group... [body...] + This macro defines NAME as a transient prefix command and binds the + transient’s infix and suffix commands. + + ARGLIST are the arguments that the prefix command takes. DOCSTRING + is the documentation string and is optional. + + These arguments can optionally be followed by keyword-value pairs. + Each key has to be a keyword symbol, either ‘:class’ or a keyword + argument supported by the constructor of that class. The + ‘transient-prefix’ class is used if the class is not specified + explicitly. + + GROUPs add key bindings for infix and suffix commands and specify + how these bindings are presented in the popup buffer. At least one + GROUP has to be specified. See *note Binding Suffix and Infix + Commands::. + + The BODY is optional. If it is omitted, then ARGLIST is ignored + and the function definition becomes: + + (lambda () + (interactive) + (transient-setup 'NAME)) + + If BODY is specified, then it must begin with an ‘interactive’ form + that matches ARGLIST, and it must call ‘transient-setup’. It may, + however, call that function only when some condition is satisfied. + + All transients have a (possibly ‘nil’) value, which is exported + when suffix commands are called, so that they can consume that + value. For some transients it might be necessary to have a sort of + secondary value, called a “scope”. Such a scope would usually be + set in the command’s ‘interactive’ form and has to be passed to the + setup function: + + (transient-setup 'NAME nil nil :scope SCOPE) + + For example, the scope of the ‘magit-branch-configure’ transient is + the branch whose variables are being configured. + + +File: transient.info, Node: Binding Suffix and Infix Commands, Next: Defining Suffix and Infix Commands, Prev: Defining Transients, Up: Defining New Commands + +4.3 Binding Suffix and Infix Commands +===================================== + +The macro ‘transient-define-prefix’ is used to define a transient. This +defines the actual transient prefix command (see *note Defining +Transients::) and adds the transient’s infix and suffix bindings, as +described below. + + Users and third-party packages can add additional bindings using +functions such as ‘transient-insert-suffix’ (see *note Modifying +Existing Transients::). These functions take a “suffix specification” +as one of their arguments, which has the same form as the specifications +used in ‘transient-define-prefix’. + +* Menu: + +* Group Specifications:: +* Suffix Specifications:: + + +File: transient.info, Node: Group Specifications, Next: Suffix Specifications, Up: Binding Suffix and Infix Commands + +4.3.1 Group Specifications +-------------------------- + +The suffix and infix commands of a transient are organized in groups. +The grouping controls how the descriptions of the suffixes are outlined +visually but also makes it possible to set certain properties for a set +of suffixes. + + Several group classes exist, some of which organize suffixes in +subgroups. In most cases the class does not have to be specified +explicitly, but see *note Group Classes::. + + Groups are specified in the call to ‘transient-define-prefix’, using +vectors. Because groups are represented using vectors, we cannot use +square brackets to indicate an optional element and instead use curly +brackets to do the latter. + + Group specifications then have this form: + + [{LEVEL} {DESCRIPTION} {KEYWORD VALUE}... ELEMENT...] + + The LEVEL is optional and defaults to 4. See *note Enabling and +Disabling Suffixes::. + + The DESCRIPTION is optional. If present, it is used as the heading +of the group. + + The KEYWORD-VALUE pairs are optional. Each keyword has to be a +keyword symbol, either ‘:class’ or a keyword argument supported by the +constructor of that class. + + • One of these keywords, ‘:description’, is equivalent to specifying + DESCRIPTION at the very beginning of the vector. The + recommendation is to use ‘:description’ if some other keyword is + also used, for consistency, or DESCRIPTION otherwise, because it + looks better. + + • Likewise ‘:level’ is equivalent to LEVEL. + + • Other important keywords include the ‘:if...’ keywords. These + keywords control whether the group is available in a certain + situation. + + For example, one group of the ‘magit-rebase’ transient uses ‘:if + magit-rebase-in-progress-p’, which contains the suffixes that are + useful while rebase is already in progress; and another that uses + ‘:if-not magit-rebase-in-progress-p’, which contains the suffixes + that initiate a rebase. + + These predicates can also be used on individual suffixes and are + only documented once, see *note Predicate Slots::. + + • The value of ‘:hide’, if non-‘nil’, is a predicate that controls + whether the group is hidden by default. The key bindings for + suffixes of a hidden group should all use the same prefix key. + Pressing that prefix key should temporarily show the group and its + suffixes, which assumes that a predicate like this is used: + + (lambda () + (eq (car transient--redisplay-key) + ?\C-c)) ; the prefix key shared by all bindings + + • The value of ‘:setup-children’, if non-‘nil’, is a function that + takes one argument, a potentially list of children, and must return + a list of children or an empty list. This can either be used to + somehow transform the group’s children that were defined the normal + way, or to dynamically create the children from scratch. + + The returned children must have the same form as stored in the + prefix’s ‘transient--layout’ property, but it is often more + convenient to use the same form as understood by + ‘transient-define-prefix’, described below. If you use the latter + approach, you can use the ‘transient-parse-suffixes’ and + ‘transient-parse-suffix’ functions to transform them from the + convenient to the expected form. + + If you explicitly specify children and then transform them using + ‘:setup-chilren’, then the class of the group is determined as + usual, based on explicitly specified children. + + If you do not explicitly specify children and thus rely solely on + ‘:setup-children’, then you must specify the class using ‘:class’. + For backward compatibility, if you fail to do so, + ‘transient-column’ is used and a warning is displayed. This + warning will eventually be replaced with an error. + + • The boolean ‘:pad-keys’ argument controls whether keys of all + suffixes contained in a group are right padded, effectively + aligning the descriptions. + + The ELEMENTs are either all subgroups, or all suffixes and strings. +(At least currently no group type exists that would allow mixing +subgroups with commands at the same level, though in principle there is +nothing that prevents that.) + + If the ELEMENTs are not subgroups, then they can be a mixture of +lists, which specify commands, and strings. Strings are inserted +verbatim into the buffer. The empty string can be used to insert gaps +between suffixes, which is particularly useful if the suffixes are +outlined as a table. + + Inside group specifications, including inside contained suffix +specifications, nothing has to be quoted and quoting anyway is invalid. +The value following a keyword, can be explicitly unquoted using ‘,’. +This feature is experimental and should be avoided. + + The form of suffix specifications is documented in the next node. + + +File: transient.info, Node: Suffix Specifications, Prev: Group Specifications, Up: Binding Suffix and Infix Commands + +4.3.2 Suffix Specifications +--------------------------- + +A transient’s suffix and infix commands are bound when the transient +prefix command is defined using ‘transient-define-prefix’, see *note +Defining Transients::. The commands are organized into groups, see +*note Group Specifications::. Here we describe the form used to bind an +individual suffix command. + + The same form is also used when later binding additional commands +using functions such as ‘transient-insert-suffix’, see *note Modifying +Existing Transients::. + + Note that an infix is a special kind of suffix. Depending on context +“suffixes” means “suffixes (including infixes)” or “non-infix suffixes”. +Here it means the former. + + Suffix specifications have this form: + + ([LEVEL] [KEY [DESCRIPTION]] COMMAND|ARGUMENT [KEYWORD VALUE]...) + + LEVEL, KEY and DESCRIPTION can also be specified using the KEYWORDs +‘:level’, ‘:key’ and ‘:description’. If the object that is associated +with COMMAND sets these properties, then they do not have to be +specified here. You can however specify them here anyway, possibly +overriding the object’s values just for the binding inside this +transient. + + • LEVEL is the suffix level, an integer between 1 and 7. See *note + Enabling and Disabling Suffixes::. + + • KEY is the key binding, either a vector or key description string. + + • DESCRIPTION is the description, either a string or a function that + takes zero or one arguments (the suffix object) and returns a + string. The function should be a lambda expression to avoid + ambiguity. In some cases a symbol that is bound as a function + would also work but to be safe you should use ‘:description’ in + that case. + + The next element is either a command or an argument. This is the +only argument that is mandatory in all cases. + + • COMMAND should be a symbol that is bound as a function, which has + to be defined or at least autoloaded as a command by the time the + containing prefix command is invoked. + + Any command will do; it does not need to have an object associated + with it (as would be the case if ‘transient-define-suffix’ or + ‘transient-define-infix’ were used to define it). + + COMMAND can also be a ‘lambda’ expression. + + As mentioned above, the object that is associated with a command + can be used to set the default for certain values that otherwise + have to be set in the suffix specification. Therefore if there is + no object, then you have to make sure to specify the KEY and the + DESCRIPTION. + + As a special case, if you want to add a command that might be + neither defined nor autoloaded, you can use a workaround like: + + (transient-insert-suffix 'some-prefix "k" + '("!" "Ceci n'est pas une commande" no-command + :if (lambda () (featurep 'no-library)))) + + Instead of ‘featurep’ you could also use ‘require’ with a non-‘nil’ + value for NOERROR. + + • The mandatory argument can also be a command-line argument, a + string. In that case an anonymous command is defined and bound. + + Instead of a string, this can also be a list of two strings, in + which case the first string is used as the short argument (which + can also be specified using ‘:shortarg’) and the second as the long + argument (which can also be specified using ‘:argument’). + + Only the long argument is displayed in the popup buffer. See + ‘transient-detect-key-conflicts’ for how the short argument may be + used. + + Unless the class is specified explicitly, the appropriate class is + guessed based on the long argument. If the argument ends with ‘=’ + (e.g., ‘--format=’) then ‘transient-option’ is used, otherwise + ‘transient-switch’. + + Finally, details can be specified using optional KEYWORD-VALUE pairs. +Each keyword has to be a keyword symbol, either ‘:class’ or a keyword +argument supported by the constructor of that class. See *note Suffix +Slots::. + + +File: transient.info, Node: Defining Suffix and Infix Commands, Next: Using Infix Arguments, Prev: Binding Suffix and Infix Commands, Up: Defining New Commands + +4.4 Defining Suffix and Infix Commands +====================================== + +Note that an infix is a special kind of suffix. Depending on context +“suffixes” means “suffixes (including infixes)” or “non-infix suffixes”. + + -- Macro: transient-define-suffix name arglist [docstring] [keyword + value]... body... + This macro defines NAME as a transient suffix command. + + ARGLIST are the arguments that the command takes. DOCSTRING is the + documentation string and is optional. + + These arguments can optionally be followed by keyword-value pairs. + Each keyword has to be a keyword symbol, either ‘:class’ or a + keyword argument supported by the constructor of that class. The + ‘transient-suffix’ class is used if the class is not specified + explicitly. + + The BODY must begin with an ‘interactive’ form that matches + ARGLIST. The infix arguments are usually accessed by using + ‘transient-args’ inside ‘interactive’. + + -- Macro: transient-define-infix name arglist [docstring] [keyword + value]... + This macro defines NAME as a transient infix command. + + ARGLIST is always ignored (but mandatory never-the-less) and + reserved for future use. DOCSTRING is the documentation string and + is optional. + + At least one key-value pair is required. All transient infix + commands are ‘equal’ to each other (but not ‘eq’). It is + meaningless to define an infix command, without providing at least + one keyword argument (usually ‘:argument’ or ‘:variable’, depending + on the class). The suffix class defaults to ‘transient-switch’ and + can be set using the ‘:class’ keyword. + + The function definition is always: + + (lambda () + (interactive) + (let ((obj (transient-suffix-object))) + (transient-infix-set obj (transient-infix-read obj))) + (transient--show)) + + ‘transient-infix-read’ and ‘transient-infix-set’ are generic + functions. Different infix commands behave differently because the + concrete methods are different for different infix command classes. + In rare cases the above command function might not be suitable, + even if you define your own infix command class. In that case you + have to use ‘transient-define-suffix’ to define the infix command + and use ‘t’ as the value of the ‘:transient’ keyword. + + -- Macro: transient-define-argument name arglist [docstring] [keyword + value]... + This macro defines NAME as a transient infix command. + + This is an alias for ‘transient-define-infix’. Only use this alias + to define an infix command that actually sets an infix argument. + To define an infix command that, for example, sets a variable, use + ‘transient-define-infix’ instead. + + +File: transient.info, Node: Using Infix Arguments, Next: Transient State, Prev: Defining Suffix and Infix Commands, Up: Defining New Commands + +4.5 Using Infix Arguments +========================= + +The functions and the variables described below allow suffix commands to +access the value of the transient from which they were invoked; which is +the value of its infix arguments. These variables are set when the user +invokes a suffix command that exits the transient, but before actually +calling the command. + + When returning to the command-loop after calling the suffix command, +the arguments are reset to ‘nil’ (which causes the function to return +‘nil’ too). + + Like for Emacs’ prefix arguments, it is advisable, but not mandatory, +to access the infix arguments inside the command’s ‘interactive’ form. +The preferred way of doing that is to call the ‘transient-args’ +function, which for infix arguments serves about the same purpose as +‘prefix-arg’ serves for prefix arguments. + + -- Function: transient-args prefix + This function returns the value of the transient prefix command + PREFIX. + + If the current command was invoked from the transient prefix + command PREFIX, then it returns the active infix arguments. If the + current command was not invoked from PREFIX, then it returns the + set, saved or default value for PREFIX. + + -- Function: transient-arg-value arg args + This function return the value of ARG as it appears in ARGS. + + For a switch a boolean is returned. For an option the value is + returned as a string, using the empty string for the empty value, + or ‘nil’ if the option does not appear in ARGS. + + -- Function: transient-suffixes prefix + This function returns the suffixes of the transient prefix command + PREFIX. This is a list of objects. This function should only be + used if you need the objects (as opposed to just their values) and + if the current command is not being invoked from PREFIX. + + -- Variable: transient-current-suffixes + The suffixes of the transient from which this suffix command was + invoked. This is a list of objects. Usually it is sufficient to + instead use the function ‘transient-args’, which returns a list of + values. In complex cases it might be necessary to use this + variable instead, i.e., if you need access to information beside + the value. + + -- Variable: transient-current-prefix + The transient from which this suffix command was invoked. The + returned value is a ‘transient-prefix’ object, which holds + information associated with the transient prefix command. + + -- Variable: transient-current-command + The transient from which this suffix command was invoked. The + returned value is a symbol, the transient prefix command. + + +File: transient.info, Node: Transient State, Prev: Using Infix Arguments, Up: Defining New Commands + +4.6 Transient State +=================== + +Invoking a transient prefix command “activates” the respective +transient, i.e., it puts a transient keymap into effect, which binds the +transient’s infix and suffix commands. + + The default behavior while a transient is active is as follows: + + • Invoking an infix command does not affect the transient state; the + transient remains active. + + • Invoking a (non-infix) suffix command “deactivates” the transient + state by removing the transient keymap and performing some + additional cleanup. + + • Invoking a command that is bound in a keymap other than the + transient keymap is disallowed and trying to do so results in a + warning. This does not “deactivate” the transient. + + The behavior can be changed for all suffixes of a particular prefix +and/or for individual suffixes. The values should nearly always be +booleans, but certain functions, called “pre-commands”, can also be +used. These functions are named ‘transient--do-VERB’, and the symbol +‘VERB’ can be used as a shorthand. + + A boolean is interpreted as answering the question "does the +transient stay active, when this command is invoked?" ‘t’ means that +the transient stays active, while ‘nil’ means that invoking the command +exits the transient. + + Note that when the suffix is a “sub-prefix”, invoking that command +always activates that sub-prefix, causing the outer prefix to no longer +be active and displayed. Here ‘t’ means that when you exit the inner +prefix, then the outer prefix becomes active again, while ‘nil’ means +that all outer prefixes are exited at once. + + • The behavior for non-suffixes can be set for a particular prefix, + by the prefix’s ‘transient-non-suffix’ slot to a boolean, a + suitable pre-command function, or a shorthand for such a function. + See *note Pre-commands for Non-Suffixes::. + + • The common behavior for the suffixes of a particular prefix can be + set using the prefix’s ‘transient-suffixes’ slot. + + The value specified in this slot does *not* affect infixes. + Because it affects both regular suffixes as well as sub-prefixes, + which have different needs, it is best to avoid explicitly + specifying a function. + + • The behavior of an individual suffix can be changed using its + ‘transient’ slot. While it is usually best to use a boolean, for + this slot it can occasionally make sense to specify a function + explicitly. + + Note that this slot can be set when defining a suffix command using + ‘transient-define-suffix’ and/or in the definition of the prefix. + If set in both places, then the latter takes precedence, as usual. + + The available pre-command functions are documented in the following +sub-sections. They are called by ‘transient--pre-command’, a function +on ‘pre-command-hook’, and the value that they return determines whether +the transient is exited. To do so the value of one of the constants +‘transient--exit’ or ‘transient--stay’ is used (that way we don’t have +to remember if ‘t’ means “exit” or “stay”). + + Additionally, these functions may change the value of ‘this-command’ +(which explains why they have to be called using ‘pre-command-hook’), +call ‘transient-export’, ‘transient--stack-zap’ or +‘transient--stack-push’; and set the values of ‘transient--exitp’, +‘transient--helpp’ or ‘transient--editp’. + + For completeness sake, some notes about complications: + + • The transient-ness of certain built-in suffix commands is specified + using ‘transient-predicate-map’. This is a special keymap, which + binds commands to pre-commands (as opposed to keys to commands) and + takes precedence over the prefix’s ‘transient-suffix’ slot, but not + the suffix’s ‘transient’ slot. + + • While a sub-prefix is active we nearly always want ‘C-g’ to take + the user back to the “super-prefix”, even when the other suffixes + don’t do that. However, in rare cases this may not be desirable, + and that makes the following complication necessary: + + For ‘transient-suffix’ objects the ‘transient’ slot is unbound. We + can ignore that for the most part because ‘nil’ and the slot being + unbound are treated as equivalent, and mean “do exit”. That isn’t + actually true for suffixes that are sub-prefixes though. For such + suffixes unbound means “do exit but allow going back”, which is the + default, while ‘nil’ means “do exit permanently”, which requires + that slot to be explicitly set to that value. + +Pre-commands for Infixes +------------------------ + +The default for infixes is ‘transient--do-stay’. This is also the only +function that makes sense for infixes, which is why this predicate is +used even if the value of the prefix’s ‘transient-suffix’ slot is ‘t’. +In extremely rare cases, one might want to use something else, which can +be done by setting the infix’s ‘transient’ slot directly. + + -- Function: transient--do-stay + Call the command without exporting variables and stay transient. + +Pre-commands for Suffixes +------------------------- + +By default, invoking a suffix causes the transient to be exited. + + The behavior for an individual suffix command can be changed by +setting its ‘transient’ slot to a boolean (which is highly recommended), +or to one of the following pre-commands. + + -- Function: transient--do-exit + Call the command after exporting variables and exit the transient. + + -- Function: transient--do-return + Call the command after exporting variables and return to the parent + prefix. If there is no parent prefix, then call + ‘transient--do-exit’. + + -- Function: transient--do-call + Call the command after exporting variables and stay transient. + + The following pre-commands are only suitable for sub-prefixes. It is +not necessary to explicitly use these predicates because the correct +predicate is automatically picked based on the value of the ‘transient’ +slot for the sub-prefix itself. + + -- Function: transient--do-recurse + Call the transient prefix command, preparing for return to active + transient. + + Whether we actually return to the parent transient is ultimately + under the control of each invoked suffix. The difference between + this pre-command and ‘transient--do-stack’ is that it changes the + value of the ‘transient-suffix’ slot to ‘t’. + + If there is no parent transient, then only call this command and + skip the second step. + + -- Function: transient--do-stack + Call the transient prefix command, stacking the active transient. + Push the active transient to the transient stack. + + Unless ‘transient--do-recurse’ is explicitly used, this pre-command + is automatically used for suffixes that are prefixes themselves, + i.e., for sub-prefixes. + + -- Function: transient--do-replace + Call the transient prefix command, replacing the active transient. + Do not push the active transient to the transient stack. + + Unless ‘transient--do-recurse’ is explicitly used, this pre-command + is automatically used for suffixes that are prefixes themselves, + i.e., for sub-prefixes. + + -- Function: transient--do-suspend + Suspend the active transient, saving the transient stack. + + This is used by the command ‘transient-suspend’ and optionally also + by “external events” such as ‘handle-switch-frame’. Such bindings + should be added to ‘transient-predicate-map’. + +Pre-commands for Non-Suffixes +----------------------------- + +By default, non-suffixes (commands that are bound in other keymaps +beside the transient keymap) cannot be invoked. Trying to invoke such a +command results in a warning and the transient stays active. + + If you want a different behavior, then set the ‘transient-non-suffix’ +slot of the transient prefix command. The value should be a boolean, +answering the question, "is it allowed to invoke non-suffix commands?, a +pre-command function, or a shorthand for such a function. + + If the value is ‘t’, then non-suffixes can be invoked, when it is +‘nil’ (the default) then they cannot be invoked. + + The only other recommended value is ‘leave’. If that is used, then +non-suffixes can be invoked, but if one is invoked, then that exits the +transient. + + -- Function: transient--do-warn + Call ‘transient-undefined’ and stay transient. + + -- Function: transient--do-stay + Call the command without exporting variables and stay transient. + + -- Function: transient--do-leave + Call the command without exporting variables and exit the + transient. + +Special Pre-Commands +-------------------- + + -- Function: transient--do-quit-one + If active, quit help or edit mode, else exit the active transient. + + This is used when the user pressed ‘C-g’. + + -- Function: transient--do-quit-all + Exit all transients without saving the transient stack. + + This is used when the user pressed ‘C-q’. + + -- Function: transient--do-suspend + Suspend the active transient, saving the transient stack. + + This is used when the user pressed ‘C-z’. + + +File: transient.info, Node: Classes and Methods, Next: FAQ, Prev: Defining New Commands, Up: Top + +5 Classes and Methods +********************* + +Transient uses classes and generic functions to make it possible to +define new types of suffix commands that are similar to existing types, +but behave differently in some aspects. It does the same for groups and +prefix commands, though at least for prefix commands that *currently* +appears to be less important. + + Every prefix, infix and suffix command is associated with an object, +which holds information that controls certain aspects of its behavior. +This happens in two ways. + + • Associating a command with a certain class gives the command a + type. This makes it possible to use generic functions to do + certain things that have to be done differently depending on what + type of command it acts on. + + That in turn makes it possible for third-parties to add new types + without having to convince the maintainer of Transient that that + new type is important enough to justify adding a special case to a + dozen or so functions. + + • Associating a command with an object makes it possible to easily + store information that is specific to that particular command. + + Two commands may have the same type, but obviously their key + bindings and descriptions still have to be different, for example. + + The values of some slots are functions. The ‘reader’ slot for + example holds a function that is used to read a new value for an + infix command. The values of such slots are regular functions. + + Generic functions are used when a function should do something + different based on the type of the command, i.e., when all commands + of a certain type should behave the same way but different from the + behavior for other types. Object slots that hold a regular + function as value are used when the task that they perform is + likely to differ even between different commands of the same type. + +* Menu: + +* Group Classes:: +* Group Methods:: +* Prefix Classes:: +* Suffix Classes:: +* Suffix Methods:: +* Prefix Slots:: +* Suffix Slots:: +* Predicate Slots:: + + +File: transient.info, Node: Group Classes, Next: Group Methods, Up: Classes and Methods + +5.1 Group Classes +================= + +The type of a group can be specified using the ‘:class’ property at the +beginning of the class specification, e.g., ‘[:class transient-columns +...]’ in a call to ‘transient-define-prefix’. + + • The abstract ‘transient-child’ class is the base class of both + ‘transient-group’ (and therefore all groups) as well as of + ‘transient-suffix’ (and therefore all suffix and infix commands). + + This class exists because the elements (or “children”) of certain + groups can be other groups instead of suffix and infix commands. + + • The abstract ‘transient-group’ class is the superclass of all other + group classes. + + • The ‘transient-column’ class is the simplest group. + + This is the default “flat” group. If the class is not specified + explicitly and the first element is not a vector (i.e., not a + group), then this class is used. + + This class displays each element on a separate line. + + • The ‘transient-row’ class displays all elements on a single line. + + • The ‘transient-columns’ class displays commands organized in + columns. + + Direct elements have to be groups whose elements have to be + commands or strings. Each subgroup represents a column. This + class takes care of inserting the subgroups’ elements. + + This is the default “nested” group. If the class is not specified + explicitly and the first element is a vector (i.e., a group), then + this class is used. + + • The ‘transient-subgroups’ class wraps other groups. + + Direct elements have to be groups whose elements have to be + commands or strings. This group inserts an empty line between + subgroups. The subgroups themselves are responsible for displaying + their elements. + + +File: transient.info, Node: Group Methods, Next: Prefix Classes, Prev: Group Classes, Up: Classes and Methods + +5.2 Group Methods +================= + + -- Function: transient-setup-children group children + This generic function can be used to setup the children or a group. + + The default implementation usually just returns the children + unchanged, but if the ‘setup-children’ slot of GROUP is non-‘nil’, + then it calls that function with CHILDREN as the only argument and + returns the value. + + The children are given as a (potentially empty) list consisting of + either group or suffix specifications. These functions can make + arbitrary changes to the children including constructing new + children from scratch. + + -- Function: transient--insert-group group + This generic function formats the group and its elements and + inserts the result into the current buffer, which is a temporary + buffer. The contents of that buffer are later inserted into the + popup buffer. + + Functions that are called by this function may need to operate in + the buffer from which the transient was called. To do so they can + temporarily make the ‘transient--source-buffer’ the current buffer. + + +File: transient.info, Node: Prefix Classes, Next: Suffix Classes, Prev: Group Methods, Up: Classes and Methods + +5.3 Prefix Classes +================== + +Currently the ‘transient-prefix’ class is being used for all prefix +commands and there is only a single generic function that can be +specialized based on the class of a prefix command. + + -- Function: transient--history-init obj + This generic function is called while setting up the transient and + is responsible for initializing the ‘history’ slot. This is the + transient-wide history; many individual infixes also have a history + of their own. + + The default (and currently only) method extracts the value from the + global variable ‘transient-history’. + + A transient prefix command’s object is stored in the +‘transient--prefix’ property of the command symbol. While a transient +is active, a clone of that object is stored in the variable +‘transient--prefix’. A clone is used because some changes that are made +to the active transient’s object should not affect later invocations. + + +File: transient.info, Node: Suffix Classes, Next: Suffix Methods, Prev: Prefix Classes, Up: Classes and Methods + +5.4 Suffix Classes +================== + + • All suffix and infix classes derive from ‘transient-suffix’, which + in turn derives from ‘transient-child’, from which + ‘transient-group’ also derives (see *note Group Classes::). + + • All infix classes derive from the abstract ‘transient-infix’ class, + which in turn derives from the ‘transient-suffix’ class. + + Infixes are a special type of suffixes. The primary difference is + that infixes always use the ‘transient--do-stay’ pre-command, while + non-infix suffixes use a variety of pre-commands (see *note + Transient State::). Doing that is most easily achieved by using + this class, though theoretically it would be possible to define an + infix class that does not do so. If you do that then you get to + implement many methods. + + Also, infixes and non-infix suffixes are usually defined using + different macros (see *note Defining Suffix and Infix Commands::). + + • Classes used for infix commands that represent arguments should be + derived from the abstract ‘transient-argument’ class. + + • The ‘transient-switch’ class (or a derived class) is used for infix + arguments that represent command-line switches (arguments that do + not take a value). + + • The ‘transient-option’ class (or a derived class) is used for infix + arguments that represent command-line options (arguments that do + take a value). + + • The ‘transient-switches’ class can be used for a set of mutually + exclusive command-line switches. + + • The ‘transient-files’ class can be used for a ‘--’ argument that + indicates that all remaining arguments are files. + + • Classes used for infix commands that represent variables should + derived from the abstract ‘transient-variable’ class. + + • The ‘transient-information’ class is special in that suffixes that + use this class are not associated with a command and thus also not + with any key binding. Such suffixes are only used to display + arbitrary information, and that anywhere a suffix can appear. + Display-only suffix specifications take this form: + + ([LEVEL] :info DESCRIPTION [KEYWORD VALUE]...) + + The ‘:info’ keyword argument replaces the ‘:description’ keyword + used for other suffix classes. Other keyword arguments that you + might want to set, include ‘:face’, predicate keywords (such as + ‘:if’), and ‘:format’. By default the value of ‘:format’ includes + ‘%k’, which for this class is replaced with the empty string or + spaces, if keys are being padded in the containing group. + + Magit defines additional classes, which can serve as examples for the +fancy things you can do without modifying Transient. Some of these +classes will likely get generalized and added to Transient. For now +they are very much subject to change and not documented. + + +File: transient.info, Node: Suffix Methods, Next: Prefix Slots, Prev: Suffix Classes, Up: Classes and Methods + +5.5 Suffix Methods +================== + +To get information about the methods implementing these generic +functions use ‘describe-function’. + +* Menu: + +* Suffix Value Methods:: +* Suffix Format Methods:: + + +File: transient.info, Node: Suffix Value Methods, Next: Suffix Format Methods, Up: Suffix Methods + +5.5.1 Suffix Value Methods +-------------------------- + + -- Function: transient-init-value obj + This generic function sets the initial value of the object OBJ. + + This function is called for all suffix commands, but unless a + concrete method is implemented this falls through to the default + implementation, which is a noop. In other words this usually only + does something for infix commands, but note that this is not + implemented for the abstract class ‘transient-infix’, so if your + class derives from that directly, then you must implement a method. + + -- Function: transient-infix-read obj + This generic function determines the new value of the infix object + OBJ. + + This function merely determines the value; ‘transient-infix-set’ is + used to actually store the new value in the object. + + For most infix classes this is done by reading a value from the + user using the reader specified by the ‘reader’ slot (using the + ‘transient-infix-value’ method described below). + + For some infix classes the value is changed without reading + anything in the minibuffer, i.e., the mere act of invoking the + infix command determines what the new value should be, based on the + previous value. + + -- Function: transient-prompt obj + This generic function returns the prompt to be used to read infix + object OBJ’s value. + + -- Function: transient-infix-set obj value + This generic function sets the value of infix object OBJ to VALUE. + + -- Function: transient-infix-value obj + This generic function returns the value of the suffix object OBJ. + + This function is called by ‘transient-args’ (which see), meaning + this function is how the value of a transient is determined so that + the invoked suffix command can use it. + + Currently most values are strings, but that is not set in stone. + ‘nil’ is not a value, it means “no value”. + + Usually only infixes have a value, but see the method for + ‘transient-suffix’. + + -- Function: transient-init-scope obj + This generic function sets the scope of the suffix object OBJ. + + The scope is actually a property of the transient prefix, not of + individual suffixes. However it is possible to invoke a suffix + command directly instead of from a transient. In that case, if the + suffix expects a scope, then it has to determine that itself and + store it in its ‘scope’ slot. + + This function is called for all suffix commands, but unless a + concrete method is implemented this falls through to the default + implementation, which is a noop. + + +File: transient.info, Node: Suffix Format Methods, Prev: Suffix Value Methods, Up: Suffix Methods + +5.5.2 Suffix Format Methods +--------------------------- + + -- Function: transient-format obj + This generic function formats and returns OBJ for display. + + When this function is called, then the current buffer is some + temporary buffer. If you need the buffer from which the prefix + command was invoked to be current, then do so by temporarily making + ‘transient--source-buffer’ current. + + -- Function: transient-format-key obj + This generic function formats OBJ’s ‘key’ for display and returns + the result. + + -- Function: transient-format-description obj + This generic function formats OBJ’s ‘description’ for display and + returns the result. + + -- Function: transient-format-value obj + This generic function formats OBJ’s value for display and returns + the result. + + -- Function: transient-show-help obj + Show help for the prefix, infix or suffix command represented by + OBJ. + + For prefixes, show the info manual, if that is specified using the + ‘info-manual’ slot. Otherwise, show the manpage if that is + specified using the ‘man-page’ slot. Otherwise, show the command’s + documentation string. + + For suffixes, show the command’s documentation string. + + For infixes, show the manpage if that is specified. Otherwise show + the command’s documentation string. + + +File: transient.info, Node: Prefix Slots, Next: Suffix Slots, Prev: Suffix Methods, Up: Classes and Methods + +5.6 Prefix Slots +================ + + • ‘show-help’, ‘man-page’ or ‘info-manual’ can be used to specify the + documentation for the prefix and its suffixes. The command + ‘transient-help’ uses the method ‘transient-show-help’ (which see) + to lookup and use these values. + + • ‘history-key’ If multiple prefix commands should share a single + value, then this slot has to be set to the same value for all of + them. You probably don’t want that. + + • ‘transient-suffix’ and ‘transient-non-suffix’ play a part when + determining whether the currently active transient prefix command + remains active/transient when a suffix or arbitrary non-suffix + command is invoked. See *note Transient State::. + + • ‘refresh-suffixes’ Normally suffix objects and keymaps are only + setup once, when the prefix is invoked. Setting this to ‘t’, + causes them to be recreated after every command. This is useful + when using ‘:if...’ predicates, and those need to be rerun for some + reason. Doing this is somewhat costly, and there is a risk of + losing state, so this is disabled by default and still considered + experimental. + + • ‘incompatible’ A list of lists. Each sub-list specifies a set of + mutually exclusive arguments. Enabling one of these arguments + causes the others to be disabled. An argument may appear in + multiple sub-lists. Arguments must me given in the same form as + used in the ‘argument’ or ‘argument-format’ slot of the respective + suffix objects, usually something like ‘--switch’ or ‘--option=%s’. + For options and ‘transient-switches’ suffixes it is also possible + to match against a specific value, as returned by + ‘transient-infix-value’, for example, ‘--option=one’. + + • ‘scope’ For some transients it might be necessary to have a sort of + secondary value, called a “scope”. See ‘transient-define-prefix’. + +Internal Prefix Slots +--------------------- + +These slots are mostly intended for internal use. They should not be +set in calls to ‘transient-define-prefix’. + + • ‘prototype’ When a transient prefix command is invoked, then a + clone of that object is stored in the global variable + ‘transient--prefix’ and the prototype is stored in the clone’s + ‘prototype’ slot. + + • ‘command’ The command, a symbol. Each transient prefix command + consists of a command, which is stored in a symbol’s function slot + and an object, which is stored in the ‘transient--prefix’ property + of the same symbol. + + • ‘level’ The level of the prefix commands. The suffix commands + whose layer is equal or lower are displayed. See *note Enabling + and Disabling Suffixes::. + + • ‘value’ The likely outdated value of the prefix. Instead of + accessing this slot directly you should use the function + ‘transient-get-value’, which is guaranteed to return the up-to-date + value. + + • ‘history’ and ‘history-pos’ are used to keep track of historic + values. Unless you implement your own ‘transient-infix-read’ + method you should not have to deal with these slots. + + +File: transient.info, Node: Suffix Slots, Next: Predicate Slots, Prev: Prefix Slots, Up: Classes and Methods + +5.7 Suffix Slots +================ + +Here we document most of the slots that are only available for suffix +objects. Some slots are shared by suffix and group objects, they are +documented in *note Predicate Slots::. + + Also see *note Suffix Classes::. + +Slots of ‘transient-suffix’ +--------------------------- + + • ‘key’ The key, a key vector or a key description string. + + • ‘command’ The command, a symbol. + + • ‘transient’ Whether to stay transient. See *note Transient + State::. + + • ‘format’ The format used to display the suffix in the popup buffer. + It must contain the following %-placeholders: + + • ‘%k’ For the key. + • ‘%d’ For the description. + • ‘%v’ For the infix value. Non-infix suffixes don’t have a + value. + + • ‘description’ The description, either a string or a function, which + is called with zero or one argument (the suffix object), and + returns a string. + + • ‘face’ Face used for the description. In simple cases it is easier + to use this instead of using a function as ‘description’ and adding + the styling there. ‘face’ is appended using + ‘add-face-text-property’. + + • ‘show-help’ A function used to display help for the suffix. If + unspecified, the prefix controls how help is displayed for its + suffixes. + +Slots of ‘transient-infix’ +-------------------------- + +Some of these slots are only meaningful for some of the subclasses. +They are defined here anyway to allow sharing certain methods. + + • ‘argument’ The long argument, e.g., ‘--verbose’. + + • ‘shortarg’ The short argument, e.g., ‘-v’. + + • ‘value’ The value. Should not be accessed directly. + + • ‘init-value’ Function that is responsible for setting the object’s + value. If bound, then this is called with the object as the only + argument. Usually this is not bound, in which case the object’s + primary ‘transient-init-value’ method is called instead. + + • ‘unsavable’ Whether the value of the suffix is not saved as part of + the prefixes. + + • ‘multi-value’ For options, whether the option can have multiple + values. If this is non-‘nil’, then the values are read using + ‘completing-read-multiple’ by default and if you specify your own + reader, then it should read the values using that function or + similar. + + Supported non-‘nil’ values are: + + • Use ‘rest’ for an option that can have multiple values. This + is useful e.g., for an ‘--’ argument that indicates that all + remaining arguments are files (such as ‘git log -- file1 + file2’). + + In the list returned by ‘transient-args’ such an option and + its values are represented by a single list of the form + ‘(ARGUMENT . VALUES)’. + + • Use ‘repeat’ for an option that can be specified multiple + times. + + In the list returned by ‘transient-args’ each instance of the + option and its value appears separately in the usual from, for + example: ‘("--another-argument" "--option=first" + "--option=second")’. + + In both cases the option’s values have to be specified in the + default value of a prefix using the same format as returned by + ‘transient-args’, e.g., ‘("--other" "--o=1" "--o=2" ("--" "f1" + "f2"))’. + + • ‘always-read’ For options, whether to read a value on every + invocation. If this is ‘nil’, then options that have a value are + simply unset and have to be invoked a second time to set a new + value. + + • ‘allow-empty’ For options, whether the empty string is a valid + value. + + • ‘history-key’ The key used to store the history. This defaults to + the command name. This is useful when multiple infixes should + share the same history because their values are of the same kind. + + • ‘reader’ The function used to read the value of an infix. Not used + for switches. The function takes three arguments, PROMPT, + INITIAL-INPUT and HISTORY, and must return a string. + + • ‘prompt’ The prompt used when reading the value, either a string or + a function that takes the object as the only argument and which + returns a prompt string. + + • ‘choices’ A list of valid values, or a function that returns such a + list. The latter is not implemented for ‘transient-switches’, + because I couldn’t think of a use-case. How exactly the choices + are used varies depending on the class of the suffix. + +Slots of ‘transient-variable’ +----------------------------- + + • ‘variable’ The variable. + +Slots of ‘transient-switches’ +----------------------------- + + • ‘argument-format’ The display format. Must contain ‘%s’, one of + the ‘choices’ is substituted for that. E.g., ‘--%s-order’. + + • ‘argument-regexp’ The regexp used to match any one of the switches. + E.g., ‘\\(--\\(topo\\|author-date\\|date\\)-order\\)’. + + +File: transient.info, Node: Predicate Slots, Prev: Suffix Slots, Up: Classes and Methods + +5.8 Predicate Slots +=================== + +Suffix and group objects share some predicate slots that control whether +a group or suffix should be available depending on some state. Only one +of these slots can be used at the same time. It is undefined what +happens if you use more than one. + + • ‘if’ Enable if predicate returns non-‘nil’. + • ‘if-not’ Enable if predicate returns ‘nil’. + • ‘if-non-nil’ Enable if variable’s value is non-‘nil’. + • ‘if-nil’ Enable if variable’s value is ‘nil’. + • ‘if-mode’ Enable if major-mode matches value. + • ‘if-not-mode’ Enable if major-mode does not match value. + • ‘if-derived’ Enable if major-mode derives from value. + • ‘if-not-derived’ Enable if major-mode does not derive from value. + + By default these predicates run when the prefix command is invoked, +but this can be changes, using the ‘refresh-suffixes’ prefix slot. See +*note Prefix Slots::. + + One more slot is shared between group and suffix classes, ‘level’. +Like the slots documented above, it is a predicate, but it is used for a +different purpose. The value has to be an integer between 1 and 7. +‘level’ controls whether a suffix or a group should be available +depending on user preference. See *note Enabling and Disabling +Suffixes::. + + +File: transient.info, Node: FAQ, Next: Keystroke Index, Prev: Classes and Methods, Up: Top + +Appendix A FAQ +************** + +A.1 Can I control how the popup buffer is displayed? +==================================================== + +Yes, see ‘transient-display-buffer-action’ in *note Configuration::. + +A.2 How can I copy text from the popup buffer? +============================================== + +To be able to mark text in Transient’s popup buffer using the mouse, you +have to add the below binding. Note that for technical reasons, the +region won’t be visualized, while doing so. After you have quit the +transient popup, you will be able to yank it in another buffer. + + (keymap-set transient-predicate-map + "<mouse-set-region>" + #'transient--do-stay) + +A.3 How can I autoload prefix and suffix commands? +================================================== + +If your package only supports Emacs 30, just prefix the definition with +‘;;;###autoload’. If your package supports released versions of Emacs, +you unfortunately have to use a long form autoload comment as described +in *note (elisp)Autoload::. + + ;;;###autoload (autoload 'magit-dispatch "magit" nil t) + (transient-define-prefix magit-dispatch () + ...) + +A.4 How does Transient compare to prefix keys and universal arguments? +====================================================================== + +See +<https://github.com/magit/transient/wiki/Comparison-with-prefix-keys-and-universal-arguments>. + +A.5 How does Transient compare to Magit-Popup and Hydra? +======================================================== + +See +<https://github.com/magit/transient/wiki/Comparison-with-other-packages>. + +A.6 Why did some of the key bindings change? +============================================ + +You may have noticed that the bindings for some of the common commands +do *not* have the prefix ‘C-x’ and that furthermore some of these +commands are grayed out while others are not. That unfortunately is a +bit confusing if the section of common commands is not shown +permanently, making the following explanation necessary. + + The purpose of usually hiding that section but showing it after the +user pressed the respective prefix key is to conserve space and not +overwhelm users with too much noise, while allowing the user to quickly +list common bindings on demand. + + That however should not keep us from using the best possible key +bindings. The bindings that do use a prefix do so to avoid wasting too +many non-prefix bindings, keeping them available for use in individual +transients. The bindings that do not use a prefix and that are *not* +grayed out are very important bindings that are *always* available, even +when invoking the “common command key prefix” or *any other* +transient-specific prefix. The non-prefix keys that *are* grayed out +however, are not available when any incomplete prefix key sequence is +active. They do not use the “common command key prefix” because it is +likely that users want to invoke them several times in a row and e.g., +‘M-p M-p M-p’ is much more convenient than ‘C-x M-p C-x M-p C-x M-p’. + + You may also have noticed that the “Set” command is bound to ‘C-x s’, +while Magit-Popup used to bind ‘C-c C-c’ instead. I have seen several +users praise the latter binding (sic), so I did not change it +willy-nilly. The reason that I changed it is that using different +prefix keys for different common commands, would have made the temporary +display of the common commands even more confusing, i.e., after pressing +‘C-c’ all the bindings that begin with the ‘C-x’ prefix would be grayed +out. + + Using a single prefix for common commands key means that all other +potential prefix keys can be used for transient-specific commands +*without* the section of common commands also popping up. ‘C-c’ in +particular is a prefix that I want to (and already do) use for Magit, +and also using that for a common command would prevent me from doing so. + + (Also see the next question.) + +A.7 Why does ‘q’ not quit popups anymore? +========================================= + +I agree that ‘q’ is a good binding for commands that quit something. +This includes quitting whatever transient is currently active, but it +also includes quitting whatever it is that some specific transient is +controlling. The transient ‘magit-blame’ for example binds ‘q’ to the +command that turns ‘magit-blame-mode’ off. + + So I had to decide if ‘q’ should quit the active transient (like +Magit-Popup used to) or whether ‘C-g’ should do that instead, so that +‘q’ could be bound in individual transient to whatever commands make +sense for them. Because all other letters are already reserved for use +by individual transients, I have decided to no longer make an exception +for ‘q’. + + If you want to get ‘q’’s old binding back then you can do so. Doing +that is a bit more complicated than changing a single key binding, so I +have implemented a function, ‘transient-bind-q-to-quit’ that makes the +necessary changes. See its documentation string for more information. + + +File: transient.info, Node: Keystroke Index, Next: Command and Function Index, Prev: FAQ, Up: Top + +Appendix B Keystroke Index +************************** + + +* Menu: + +* C-g: Aborting and Resuming Transients. + (line 27) +* C-g <1>: Aborting and Resuming Transients. + (line 27) +* C-h: Getting Help for Suffix Commands. + (line 11) +* C-M-n: Using History. (line 18) +* C-M-p: Using History. (line 13) +* C-q: Aborting and Resuming Transients. + (line 36) +* C-x a: Enabling and Disabling Suffixes. + (line 68) +* C-x C-k: Saving Values. (line 29) +* C-x C-s: Saving Values. (line 25) +* C-x l: Enabling and Disabling Suffixes. + (line 43) +* C-x n: Using History. (line 18) +* C-x p: Using History. (line 13) +* C-x s: Saving Values. (line 21) +* C-x t: Common Suffix Commands. + (line 18) +* C-z: Aborting and Resuming Transients. + (line 41) + + +File: transient.info, Node: Command and Function Index, Next: Variable Index, Prev: Keystroke Index, Up: Top + +Appendix C Command and Function Index +************************************* + + +* Menu: + +* transient--do-call: Transient State. (line 125) +* transient--do-exit: Transient State. (line 117) +* transient--do-leave: Transient State. (line 193) +* transient--do-quit-all: Transient State. (line 205) +* transient--do-quit-one: Transient State. (line 200) +* transient--do-recurse: Transient State. (line 133) +* transient--do-replace: Transient State. (line 153) +* transient--do-return: Transient State. (line 120) +* transient--do-stack: Transient State. (line 145) +* transient--do-stay: Transient State. (line 105) +* transient--do-stay <1>: Transient State. (line 190) +* transient--do-suspend: Transient State. (line 161) +* transient--do-suspend <1>: Transient State. (line 210) +* transient--do-warn: Transient State. (line 187) +* transient--history-init: Prefix Classes. (line 10) +* transient--insert-group: Group Methods. (line 19) +* transient-append-suffix: Modifying Existing Transients. + (line 51) +* transient-arg-value: Using Infix Arguments. + (line 31) +* transient-args: Using Infix Arguments. + (line 22) +* transient-define-argument: Defining Suffix and Infix Commands. + (line 57) +* transient-define-infix: Defining Suffix and Infix Commands. + (line 26) +* transient-define-prefix: Defining Transients. (line 13) +* transient-define-suffix: Defining Suffix and Infix Commands. + (line 9) +* transient-format: Suffix Format Methods. + (line 6) +* transient-format-description: Suffix Format Methods. + (line 18) +* transient-format-key: Suffix Format Methods. + (line 14) +* transient-format-value: Suffix Format Methods. + (line 22) +* transient-get-suffix: Modifying Existing Transients. + (line 73) +* transient-help: Getting Help for Suffix Commands. + (line 11) +* transient-history-next: Using History. (line 18) +* transient-history-prev: Using History. (line 13) +* transient-infix-read: Suffix Value Methods. + (line 16) +* transient-infix-set: Suffix Value Methods. + (line 36) +* transient-infix-value: Suffix Value Methods. + (line 39) +* transient-init-scope: Suffix Value Methods. + (line 52) +* transient-init-value: Suffix Value Methods. + (line 6) +* transient-insert-suffix: Modifying Existing Transients. + (line 49) +* transient-prompt: Suffix Value Methods. + (line 32) +* transient-quit-all: Aborting and Resuming Transients. + (line 36) +* transient-quit-one: Aborting and Resuming Transients. + (line 27) +* transient-quit-seq: Aborting and Resuming Transients. + (line 27) +* transient-remove-suffix: Modifying Existing Transients. + (line 70) +* transient-replace-suffix: Modifying Existing Transients. + (line 66) +* transient-reset: Saving Values. (line 29) +* transient-resume: Aborting and Resuming Transients. + (line 53) +* transient-save: Saving Values. (line 25) +* transient-scroll-down: Other Commands. (line 17) +* transient-scroll-up: Other Commands. (line 12) +* transient-set: Saving Values. (line 21) +* transient-set-level: Enabling and Disabling Suffixes. + (line 43) +* transient-setup-children: Group Methods. (line 6) +* transient-show-help: Suffix Format Methods. + (line 26) +* transient-suffix-put: Modifying Existing Transients. + (line 77) +* transient-suffixes: Using Infix Arguments. + (line 38) +* transient-suspend: Aborting and Resuming Transients. + (line 41) +* transient-toggle-common: Common Suffix Commands. + (line 18) +* transient-toggle-level-limit: Enabling and Disabling Suffixes. + (line 68) + + +File: transient.info, Node: Variable Index, Next: Concept Index, Prev: Command and Function Index, Up: Top + +Appendix D Variable Index +************************* + + +* Menu: + +* transient-align-variable-pitch: Configuration. (line 185) +* transient-current-command: Using Infix Arguments. + (line 57) +* transient-current-prefix: Using Infix Arguments. + (line 52) +* transient-current-suffixes: Using Infix Arguments. + (line 44) +* transient-default-level: Enabling and Disabling Suffixes. + (line 33) +* transient-detect-key-conflicts: Configuration. (line 210) +* transient-display-buffer-action: Configuration. (line 51) +* transient-enable-popup-navigation: Configuration. (line 36) +* transient-force-fixed-pitch: Configuration. (line 198) +* transient-force-single-column: Configuration. (line 93) +* transient-hide-during-minibuffer-read: Configuration. (line 181) +* transient-highlight-higher-levels: Configuration. (line 223) +* transient-highlight-mismatched-keys: Configuration. (line 135) +* transient-history-file: Using History. (line 33) +* transient-history-limit: Using History. (line 37) +* transient-levels-file: Enabling and Disabling Suffixes. + (line 38) +* transient-mode-line-format: Configuration. (line 102) +* transient-read-with-initial-input: Configuration. (line 174) +* transient-semantic-coloring: Configuration. (line 126) +* transient-show-common-commands: Common Suffix Commands. + (line 23) +* transient-show-popup: Configuration. (line 15) +* transient-substitute-key-function: Configuration. (line 153) +* transient-values-file: Saving Values. (line 31) + + +File: transient.info, Node: Concept Index, Next: GNU General Public License, Prev: Variable Index, Up: Top + +Appendix E Concept Index +************************ + + +* Menu: + +* aborting transients: Aborting and Resuming Transients. + (line 6) +* classes and methods: Classes and Methods. (line 6) +* command dispatchers: Technical Introduction. + (line 39) +* common suffix commands: Common Suffix Commands. + (line 6) +* defining infix commands: Defining Suffix and Infix Commands. + (line 6) +* defining suffix commands: Defining Suffix and Infix Commands. + (line 6) +* disabling suffixes: Enabling and Disabling Suffixes. + (line 6) +* enabling suffixes: Enabling and Disabling Suffixes. + (line 6) +* getting help: Getting Help for Suffix Commands. + (line 6) +* group specifications: Group Specifications. (line 6) +* invoking transients: Invoking Transients. (line 6) +* levels: Enabling and Disabling Suffixes. + (line 10) +* modifying existing transients: Modifying Existing Transients. + (line 6) +* quit transient: Aborting and Resuming Transients. + (line 6) +* resuming transients: Aborting and Resuming Transients. + (line 6) +* saving values of arguments: Saving Values. (line 6) +* scope of a transient: Defining Transients. (line 43) +* suffix specifications: Suffix Specifications. + (line 6) +* transient state: Transient State. (line 6) +* transient-level: Enabling and Disabling Suffixes. + (line 15) +* value history: Using History. (line 6) + + +File: transient.info, Node: GNU General Public License, Prev: Concept Index, Up: Top + +Appendix F GNU General Public License +************************************* + + Version 3, 29 June 2007 + + Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/> + + Everyone is permitted to copy and distribute verbatim copies of this + license document, but changing it is not allowed. + +Preamble +======== + +The GNU General Public License is a free, copyleft license for software +and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program—to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers’ and authors’ protection, the GPL clearly explains +that there is no warranty for this free software. For both users’ and +authors’ sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users’ freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS +==================== + + 0. Definitions. + + “This License” refers to version 3 of the GNU General Public + License. + + “Copyright” also means copyright-like laws that apply to other + kinds of works, such as semiconductor masks. + + “The Program” refers to any copyrightable work licensed under this + License. Each licensee is addressed as “you”. “Licensees” and + “recipients” may be individuals or organizations. + + To “modify” a work means to copy from or adapt all or part of the + work in a fashion requiring copyright permission, other than the + making of an exact copy. The resulting work is called a “modified + version” of the earlier work or a work “based on” the earlier work. + + A “covered work” means either the unmodified Program or a work + based on the Program. + + To “propagate” a work means to do anything with it that, without + permission, would make you directly or secondarily liable for + infringement under applicable copyright law, except executing it on + a computer or modifying a private copy. Propagation includes + copying, distribution (with or without modification), making + available to the public, and in some countries other activities as + well. + + To “convey” a work means any kind of propagation that enables other + parties to make or receive copies. Mere interaction with a user + through a computer network, with no transfer of a copy, is not + conveying. + + An interactive user interface displays “Appropriate Legal Notices” + to the extent that it includes a convenient and prominently visible + feature that (1) displays an appropriate copyright notice, and (2) + tells the user that there is no warranty for the work (except to + the extent that warranties are provided), that licensees may convey + the work under this License, and how to view a copy of this + License. If the interface presents a list of user commands or + options, such as a menu, a prominent item in the list meets this + criterion. + + 1. Source Code. + + The “source code” for a work means the preferred form of the work + for making modifications to it. “Object code” means any non-source + form of a work. + + A “Standard Interface” means an interface that either is an + official standard defined by a recognized standards body, or, in + the case of interfaces specified for a particular programming + language, one that is widely used among developers working in that + language. + + The “System Libraries” of an executable work include anything, + other than the work as a whole, that (a) is included in the normal + form of packaging a Major Component, but which is not part of that + Major Component, and (b) serves only to enable use of the work with + that Major Component, or to implement a Standard Interface for + which an implementation is available to the public in source code + form. A “Major Component”, in this context, means a major + essential component (kernel, window system, and so on) of the + specific operating system (if any) on which the executable work + runs, or a compiler used to produce the work, or an object code + interpreter used to run it. + + The “Corresponding Source” for a work in object code form means all + the source code needed to generate, install, and (for an executable + work) run the object code and to modify the work, including scripts + to control those activities. However, it does not include the + work’s System Libraries, or general-purpose tools or generally + available free programs which are used unmodified in performing + those activities but which are not part of the work. For example, + Corresponding Source includes interface definition files associated + with source files for the work, and the source code for shared + libraries and dynamically linked subprograms that the work is + specifically designed to require, such as by intimate data + communication or control flow between those subprograms and other + parts of the work. + + The Corresponding Source need not include anything that users can + regenerate automatically from other parts of the Corresponding + Source. + + The Corresponding Source for a work in source code form is that + same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of + copyright on the Program, and are irrevocable provided the stated + conditions are met. This License explicitly affirms your unlimited + permission to run the unmodified Program. The output from running + a covered work is covered by this License only if the output, given + its content, constitutes a covered work. This License acknowledges + your rights of fair use or other equivalent, as provided by + copyright law. + + You may make, run and propagate covered works that you do not + convey, without conditions so long as your license otherwise + remains in force. You may convey covered works to others for the + sole purpose of having them make modifications exclusively for you, + or provide you with facilities for running those works, provided + that you comply with the terms of this License in conveying all + material for which you do not control copyright. Those thus making + or running the covered works for you must do so exclusively on your + behalf, under your direction and control, on terms that prohibit + them from making any copies of your copyrighted material outside + their relationship with you. + + Conveying under any other circumstances is permitted solely under + the conditions stated below. Sublicensing is not allowed; section + 10 makes it unnecessary. + + 3. Protecting Users’ Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological + measure under any applicable law fulfilling obligations under + article 11 of the WIPO copyright treaty adopted on 20 December + 1996, or similar laws prohibiting or restricting circumvention of + such measures. + + When you convey a covered work, you waive any legal power to forbid + circumvention of technological measures to the extent such + circumvention is effected by exercising rights under this License + with respect to the covered work, and you disclaim any intention to + limit operation or modification of the work as a means of + enforcing, against the work’s users, your or third parties’ legal + rights to forbid circumvention of technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program’s source code as you + receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice; + keep intact all notices stating that this License and any + non-permissive terms added in accord with section 7 apply to the + code; keep intact all notices of the absence of any warranty; and + give all recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, + and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to + produce it from the Program, in the form of source code under the + terms of section 4, provided that you also meet all of these + conditions: + + a. The work must carry prominent notices stating that you + modified it, and giving a relevant date. + + b. The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in + section 4 to “keep intact all notices”. + + c. You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable + section 7 additional terms, to the whole of the work, and all + its parts, regardless of how they are packaged. This License + gives no permission to license the work in any other way, but + it does not invalidate such permission if you have separately + received it. + + d. If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has + interactive interfaces that do not display Appropriate Legal + Notices, your work need not make them do so. + + A compilation of a covered work with other separate and independent + works, which are not by their nature extensions of the covered + work, and which are not combined with it such as to form a larger + program, in or on a volume of a storage or distribution medium, is + called an “aggregate” if the compilation and its resulting + copyright are not used to limit the access or legal rights of the + compilation’s users beyond what the individual works permit. + Inclusion of a covered work in an aggregate does not cause this + License to apply to the other parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms + of sections 4 and 5, provided that you also convey the + machine-readable Corresponding Source under the terms of this + License, in one of these ways: + + a. Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b. Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that + product model, to give anyone who possesses the object code + either (1) a copy of the Corresponding Source for all the + software in the product that is covered by this License, on a + durable physical medium customarily used for software + interchange, for a price no more than your reasonable cost of + physically performing this conveying of source, or (2) access + to copy the Corresponding Source from a network server at no + charge. + + c. Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, + and only if you received the object code with such an offer, + in accord with subsection 6b. + + d. Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to + the Corresponding Source in the same way through the same + place at no further charge. You need not require recipients + to copy the Corresponding Source along with the object code. + If the place to copy the object code is a network server, the + Corresponding Source may be on a different server (operated by + you or a third party) that supports equivalent copying + facilities, provided you maintain clear directions next to the + object code saying where to find the Corresponding Source. + Regardless of what server hosts the Corresponding Source, you + remain obligated to ensure that it is available for as long as + needed to satisfy these requirements. + + e. Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the + general public at no charge under subsection 6d. + + A separable portion of the object code, whose source code is + excluded from the Corresponding Source as a System Library, need + not be included in conveying the object code work. + + A “User Product” is either (1) a “consumer product”, which means + any tangible personal property which is normally used for personal, + family, or household purposes, or (2) anything designed or sold for + incorporation into a dwelling. In determining whether a product is + a consumer product, doubtful cases shall be resolved in favor of + coverage. For a particular product received by a particular user, + “normally used” refers to a typical or common use of that class of + product, regardless of the status of the particular user or of the + way in which the particular user actually uses, or expects or is + expected to use, the product. A product is a consumer product + regardless of whether the product has substantial commercial, + industrial or non-consumer uses, unless such uses represent the + only significant mode of use of the product. + + “Installation Information” for a User Product means any methods, + procedures, authorization keys, or other information required to + install and execute modified versions of a covered work in that + User Product from a modified version of its Corresponding Source. + The information must suffice to ensure that the continued + functioning of the modified object code is in no case prevented or + interfered with solely because modification has been made. + + If you convey an object code work under this section in, or with, + or specifically for use in, a User Product, and the conveying + occurs as part of a transaction in which the right of possession + and use of the User Product is transferred to the recipient in + perpetuity or for a fixed term (regardless of how the transaction + is characterized), the Corresponding Source conveyed under this + section must be accompanied by the Installation Information. But + this requirement does not apply if neither you nor any third party + retains the ability to install modified object code on the User + Product (for example, the work has been installed in ROM). + + The requirement to provide Installation Information does not + include a requirement to continue to provide support service, + warranty, or updates for a work that has been modified or installed + by the recipient, or for the User Product in which it has been + modified or installed. Access to a network may be denied when the + modification itself materially and adversely affects the operation + of the network or violates the rules and protocols for + communication across the network. + + Corresponding Source conveyed, and Installation Information + provided, in accord with this section must be in a format that is + publicly documented (and with an implementation available to the + public in source code form), and must require no special password + or key for unpacking, reading or copying. + + 7. Additional Terms. + + “Additional permissions” are terms that supplement the terms of + this License by making exceptions from one or more of its + conditions. Additional permissions that are applicable to the + entire Program shall be treated as though they were included in + this License, to the extent that they are valid under applicable + law. If additional permissions apply only to part of the Program, + that part may be used separately under those permissions, but the + entire Program remains governed by this License without regard to + the additional permissions. + + When you convey a copy of a covered work, you may at your option + remove any additional permissions from that copy, or from any part + of it. (Additional permissions may be written to require their own + removal in certain cases when you modify the work.) You may place + additional permissions on material, added by you to a covered work, + for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material + you add to a covered work, you may (if authorized by the copyright + holders of that material) supplement the terms of this License with + terms: + + a. Disclaiming warranty or limiting liability differently from + the terms of sections 15 and 16 of this License; or + + b. Requiring preservation of specified reasonable legal notices + or author attributions in that material or in the Appropriate + Legal Notices displayed by works containing it; or + + c. Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked + in reasonable ways as different from the original version; or + + d. Limiting the use for publicity purposes of names of licensors + or authors of the material; or + + e. Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f. Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified + versions of it) with contractual assumptions of liability to + the recipient, for any liability that these contractual + assumptions directly impose on those licensors and authors. + + All other non-permissive additional terms are considered “further + restrictions” within the meaning of section 10. If the Program as + you received it, or any part of it, contains a notice stating that + it is governed by this License along with a term that is a further + restriction, you may remove that term. If a license document + contains a further restriction but permits relicensing or conveying + under this License, you may add to a covered work material governed + by the terms of that license document, provided that the further + restriction does not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you + must place, in the relevant source files, a statement of the + additional terms that apply to those files, or a notice indicating + where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in + the form of a separately written license, or stated as exceptions; + the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly + provided under this License. Any attempt otherwise to propagate or + modify it is void, and will automatically terminate your rights + under this License (including any patent licenses granted under the + third paragraph of section 11). + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from + that copyright holder, and you cure the violation prior to 30 days + after your receipt of the notice. + + Termination of your rights under this section does not terminate + the licenses of parties who have received copies or rights from you + under this License. If your rights have been terminated and not + permanently reinstated, you do not qualify to receive new licenses + for the same material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or + run a copy of the Program. Ancillary propagation of a covered work + occurring solely as a consequence of using peer-to-peer + transmission to receive a copy likewise does not require + acceptance. However, nothing other than this License grants you + permission to propagate or modify any covered work. These actions + infringe copyright if you do not accept this License. Therefore, + by modifying or propagating a covered work, you indicate your + acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically + receives a license from the original licensors, to run, modify and + propagate that work, subject to this License. You are not + responsible for enforcing compliance by third parties with this + License. + + An “entity transaction” is a transaction transferring control of an + organization, or substantially all assets of one, or subdividing an + organization, or merging organizations. If propagation of a + covered work results from an entity transaction, each party to that + transaction who receives a copy of the work also receives whatever + licenses to the work the party’s predecessor in interest had or + could give under the previous paragraph, plus a right to possession + of the Corresponding Source of the work from the predecessor in + interest, if the predecessor has it or can get it with reasonable + efforts. + + You may not impose any further restrictions on the exercise of the + rights granted or affirmed under this License. For example, you + may not impose a license fee, royalty, or other charge for exercise + of rights granted under this License, and you may not initiate + litigation (including a cross-claim or counterclaim in a lawsuit) + alleging that any patent claim is infringed by making, using, + selling, offering for sale, or importing the Program or any portion + of it. + + 11. Patents. + + A “contributor” is a copyright holder who authorizes use under this + License of the Program or a work on which the Program is based. + The work thus licensed is called the contributor’s “contributor + version”. + + A contributor’s “essential patent claims” are all patent claims + owned or controlled by the contributor, whether already acquired or + hereafter acquired, that would be infringed by some manner, + permitted by this License, of making, using, or selling its + contributor version, but do not include claims that would be + infringed only as a consequence of further modification of the + contributor version. For purposes of this definition, “control” + includes the right to grant patent sublicenses in a manner + consistent with the requirements of this License. + + Each contributor grants you a non-exclusive, worldwide, + royalty-free patent license under the contributor’s essential + patent claims, to make, use, sell, offer for sale, import and + otherwise run, modify and propagate the contents of its contributor + version. + + In the following three paragraphs, a “patent license” is any + express agreement or commitment, however denominated, not to + enforce a patent (such as an express permission to practice a + patent or covenant not to sue for patent infringement). To “grant” + such a patent license to a party means to make such an agreement or + commitment not to enforce a patent against the party. + + If you convey a covered work, knowingly relying on a patent + license, and the Corresponding Source of the work is not available + for anyone to copy, free of charge and under the terms of this + License, through a publicly available network server or other + readily accessible means, then you must either (1) cause the + Corresponding Source to be so available, or (2) arrange to deprive + yourself of the benefit of the patent license for this particular + work, or (3) arrange, in a manner consistent with the requirements + of this License, to extend the patent license to downstream + recipients. “Knowingly relying” means you have actual knowledge + that, but for the patent license, your conveying the covered work + in a country, or your recipient’s use of the covered work in a + country, would infringe one or more identifiable patents in that + country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or + arrangement, you convey, or propagate by procuring conveyance of, a + covered work, and grant a patent license to some of the parties + receiving the covered work authorizing them to use, propagate, + modify or convey a specific copy of the covered work, then the + patent license you grant is automatically extended to all + recipients of the covered work and works based on it. + + A patent license is “discriminatory” if it does not include within + the scope of its coverage, prohibits the exercise of, or is + conditioned on the non-exercise of one or more of the rights that + are specifically granted under this License. You may not convey a + covered work if you are a party to an arrangement with a third + party that is in the business of distributing software, under which + you make payment to the third party based on the extent of your + activity of conveying the work, and under which the third party + grants, to any of the parties who would receive the covered work + from you, a discriminatory patent license (a) in connection with + copies of the covered work conveyed by you (or copies made from + those copies), or (b) primarily for and in connection with specific + products or compilations that contain the covered work, unless you + entered into that arrangement, or that patent license was granted, + prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting + any implied license or other defenses to infringement that may + otherwise be available to you under applicable patent law. + + 12. No Surrender of Others’ Freedom. + + If conditions are imposed on you (whether by court order, agreement + or otherwise) that contradict the conditions of this License, they + do not excuse you from the conditions of this License. If you + cannot convey a covered work so as to satisfy simultaneously your + obligations under this License and any other pertinent obligations, + then as a consequence you may not convey it at all. For example, + if you agree to terms that obligate you to collect a royalty for + further conveying from those to whom you convey the Program, the + only way you could satisfy both those terms and this License would + be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have + permission to link or combine any covered work with a work licensed + under version 3 of the GNU Affero General Public License into a + single combined work, and to convey the resulting work. The terms + of this License will continue to apply to the part which is the + covered work, but the special requirements of the GNU Affero + General Public License, section 13, concerning interaction through + a network will apply to the combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new + versions of the GNU General Public License from time to time. Such + new versions will be similar in spirit to the present version, but + may differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies that a certain numbered version of the GNU + General Public License “or any later version” applies to it, you + have the option of following the terms and conditions either of + that numbered version or of any later version published by the Free + Software Foundation. If the Program does not specify a version + number of the GNU General Public License, you may choose any + version ever published by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future + versions of the GNU General Public License can be used, that + proxy’s public statement of acceptance of a version permanently + authorizes you to choose that version for the Program. + + Later license versions may give you additional or different + permissions. However, no additional obligations are imposed on any + author or copyright holder as a result of your choosing to follow a + later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY + APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE + COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE + RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. + SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES + AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR + CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE + THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA + BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD + PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER + PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF + THE POSSIBILITY OF SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided + above cannot be given local legal effect according to their terms, + reviewing courts shall apply local law that most closely + approximates an absolute waiver of all civil liability in + connection with the Program, unless a warranty or assumption of + liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS +=========================== + +How to Apply These Terms to Your New Programs +============================================= + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least the +“copyright” line and a pointer to where the full notice is found. + + ONE LINE TO GIVE THE PROGRAM'S NAME AND A BRIEF IDEA OF WHAT IT DOES. + Copyright (C) YEAR NAME OF AUTHOR + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + + Also add information on how to contact you by electronic and paper +mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + PROGRAM Copyright (C) YEAR NAME OF AUTHOR + This program comes with ABSOLUTELY NO WARRANTY; for details type ‘show w’. + This is free software, and you are welcome to redistribute it + under certain conditions; type ‘show c’ for details. + + The hypothetical commands ‘show w’ and ‘show c’ should show the +appropriate parts of the General Public License. Of course, your +program’s commands might be different; for a GUI interface, you would +use an “about box”. + + You should also get your employer (if you work as a programmer) or +school, if any, to sign a “copyright disclaimer” for the program, if +necessary. For more information on this, and how to apply and follow +the GNU GPL, see <https://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Lesser General Public License instead of this License. But first, +please read <https://www.gnu.org/licenses/why-not-lgpl.html>. + + + +Tag Table: +Node: Top763 +Node: Introduction2976 +Ref: Some things that Transient can do3504 +Ref: Complexity in CLI programs3857 +Ref: Using Transient for composing interactive commands4458 +Node: Usage6700 +Node: Invoking Transients7068 +Node: Aborting and Resuming Transients8147 +Node: Common Suffix Commands10768 +Node: Saving Values12604 +Ref: Saving Values-Footnote-113975 +Node: Using History14168 +Node: Getting Help for Suffix Commands15742 +Node: Enabling and Disabling Suffixes17120 +Node: Other Commands20408 +Node: Configuration21384 +Ref: Essential Options21664 +Ref: Accessibility Options25325 +Ref: Auxiliary Options25648 +Ref: Developer Options30611 +Node: Modifying Existing Transients31859 +Node: Defining New Commands36051 +Node: Technical Introduction36414 +Node: Defining Transients42115 +Node: Binding Suffix and Infix Commands44582 +Node: Group Specifications45440 +Node: Suffix Specifications50541 +Node: Defining Suffix and Infix Commands54754 +Node: Using Infix Arguments57802 +Node: Transient State60636 +Ref: Pre-commands for Infixes65451 +Ref: Pre-commands for Suffixes65971 +Ref: Pre-commands for Non-Suffixes68425 +Ref: Special Pre-Commands69561 +Node: Classes and Methods70069 +Node: Group Classes72253 +Node: Group Methods74180 +Node: Prefix Classes75433 +Node: Suffix Classes76524 +Node: Suffix Methods79611 +Node: Suffix Value Methods79932 +Node: Suffix Format Methods82690 +Node: Prefix Slots84169 +Ref: Internal Prefix Slots86304 +Node: Suffix Slots87561 +Ref: Slots of transient-suffix87929 +Ref: Slots of transient-infix89066 +Ref: Slots of transient-variable92362 +Ref: Slots of transient-switches92464 +Node: Predicate Slots92827 +Node: FAQ94262 +Ref: Can I control how the popup buffer is displayed?94391 +Ref: How can I copy text from the popup buffer?94572 +Ref: How can I autoload prefix and suffix commands?95066 +Ref: How does Transient compare to prefix keys and universal arguments?95540 +Ref: How does Transient compare to Magit-Popup and Hydra?95783 +Ref: Why did some of the key bindings change?95977 +Ref: Why does q not quit popups anymore?98330 +Node: Keystroke Index99433 +Node: Command and Function Index101298 +Node: Variable Index107890 +Node: Concept Index110163 +Node: GNU General Public License112899 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/emacs/elpa/with-editor-20240415.1558/with-editor-autoloads.el b/emacs/elpa/with-editor-20240415.1558/with-editor-autoloads.el @@ -1,105 +0,0 @@ -;;; with-editor-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- -;; Generated by the `loaddefs-generate' function. - -;; This file is part of GNU Emacs. - -;;; Code: - -(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) - - - -;;; Generated autoloads from with-editor.el - -(autoload 'with-editor-export-editor "with-editor" "\ -Teach subsequent commands to use current Emacs instance as editor. - -Set and export the environment variable ENVVAR, by default -\"EDITOR\". The value is automatically generated to teach -commands to use the current Emacs instance as \"the editor\". - -This works in `shell-mode', `term-mode', `eshell-mode' and -`vterm'. - -(fn &optional (ENVVAR \"EDITOR\"))" t) -(autoload 'with-editor-export-git-editor "with-editor" "\ -Like `with-editor-export-editor' but always set `$GIT_EDITOR'." t) -(autoload 'with-editor-export-hg-editor "with-editor" "\ -Like `with-editor-export-editor' but always set `$HG_EDITOR'." t) -(defvar shell-command-with-editor-mode nil "\ -Non-nil if Shell-Command-With-Editor mode is enabled. -See the `shell-command-with-editor-mode' command -for a description of this minor mode.") -(custom-autoload 'shell-command-with-editor-mode "with-editor" nil) -(autoload 'shell-command-with-editor-mode "with-editor" "\ -Teach `shell-command' to use current Emacs instance as editor. - -Teach `shell-command', and all commands that ultimately call that -command, to use the current Emacs instance as editor by executing -\"EDITOR=CLIENT COMMAND&\" instead of just \"COMMAND&\". - -CLIENT is automatically generated; EDITOR=CLIENT instructs -COMMAND to use to the current Emacs instance as \"the editor\", -assuming no other variable overrides the effect of \"$EDITOR\". -CLIENT may be the path to an appropriate emacsclient executable -with arguments, or a script which also works over Tramp. - -Alternatively you can use the `with-editor-async-shell-command', -which also allows the use of another variable instead of -\"EDITOR\". - -This is a global minor mode. If called interactively, toggle the -`Shell-Command-With-Editor mode' mode. If the prefix argument is -positive, enable the mode, and if it is zero or negative, disable -the mode. - -If called from Lisp, toggle the mode if ARG is `toggle'. Enable -the mode if ARG is nil, omitted, or is a positive number. -Disable the mode if ARG is a negative number. - -To check whether the minor mode is enabled in the current buffer, -evaluate `(default-value \\='shell-command-with-editor-mode)'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(autoload 'with-editor-async-shell-command "with-editor" "\ -Like `async-shell-command' but with `$EDITOR' set. - -Execute string \"ENVVAR=CLIENT COMMAND\" in an inferior shell; -display output, if any. With a prefix argument prompt for an -environment variable, otherwise the default \"EDITOR\" variable -is used. With a negative prefix argument additionally insert -the COMMAND's output at point. - -CLIENT is automatically generated; ENVVAR=CLIENT instructs -COMMAND to use to the current Emacs instance as \"the editor\", -assuming it respects ENVVAR as an \"EDITOR\"-like variable. -CLIENT may be the path to an appropriate emacsclient executable -with arguments, or a script which also works over Tramp. - -Also see `async-shell-command' and `shell-command'. - -(fn COMMAND &optional OUTPUT-BUFFER ERROR-BUFFER ENVVAR)" t) -(autoload 'with-editor-shell-command "with-editor" "\ -Like `shell-command' or `with-editor-async-shell-command'. -If COMMAND ends with \"&\" behave like the latter, -else like the former. - -(fn COMMAND &optional OUTPUT-BUFFER ERROR-BUFFER ENVVAR)" t) -(register-definition-prefixes "with-editor" '("server-" "shell-command--shell-command-with-editor-mode" "start-file-process--with-editor-process-filter" "with-editor")) - -;;; End of scraped data - -(provide 'with-editor-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; with-editor-autoloads.el ends here diff --git a/emacs/elpa/with-editor-20240415.1558/with-editor-pkg.el b/emacs/elpa/with-editor-20240415.1558/with-editor-pkg.el @@ -1,15 +0,0 @@ -(define-package "with-editor" "20240415.1558" "Use the Emacsclient as $EDITOR" - '((emacs "25.1") - (compat "29.1.4.1")) - :commit "1b4526453ef6bdee30635f469aa26085c02b1ac1" :authors - '(("Jonas Bernoulli" . "emacs.with-editor@jonas.bernoulli.dev")) - :maintainers - '(("Jonas Bernoulli" . "emacs.with-editor@jonas.bernoulli.dev")) - :maintainer - '("Jonas Bernoulli" . "emacs.with-editor@jonas.bernoulli.dev") - :keywords - '("processes" "terminals") - :url "https://github.com/magit/with-editor") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/with-editor-20240415.1558/with-editor.el b/emacs/elpa/with-editor-20240415.1558/with-editor.el @@ -1,984 +0,0 @@ -;;; with-editor.el --- Use the Emacsclient as $EDITOR -*- lexical-binding:t -*- - -;; Copyright (C) 2014-2024 The Magit Project Contributors - -;; Author: Jonas Bernoulli <emacs.with-editor@jonas.bernoulli.dev> -;; Homepage: https://github.com/magit/with-editor -;; Keywords: processes terminals - -;; Package-Version: 3.3.2 -;; Package-Requires: ((emacs "25.1") (compat "29.1.4.1")) - -;; SPDX-License-Identifier: GPL-3.0-or-later - -;; This file is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published -;; by the Free Software Foundation, either version 3 of the License, -;; or (at your option) any later version. -;; -;; This file is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with this file. If not, see <https://www.gnu.org/licenses/>. - -;;; Commentary: - -;; This library makes it possible to reliably use the Emacsclient as -;; the `$EDITOR' of child processes. It makes sure that they know how -;; to call home. For remote processes a substitute is provided, which -;; communicates with Emacs on standard output/input instead of using a -;; socket as the Emacsclient does. - -;; It provides the commands `with-editor-async-shell-command' and -;; `with-editor-shell-command', which are intended as replacements -;; for `async-shell-command' and `shell-command'. They automatically -;; export `$EDITOR' making sure the executed command uses the current -;; Emacs instance as "the editor". With a prefix argument these -;; commands prompt for an alternative environment variable such as -;; `$GIT_EDITOR'. To always use these variants add this to your init -;; file: -;; -;; (keymap-global-set "<remap> <async-shell-command>" -;; #'with-editor-async-shell-command) -;; (keymap-global-set "<remap> <shell-command>" -;; #'with-editor-shell-command) - -;; Alternatively use the global `shell-command-with-editor-mode', -;; which always sets `$EDITOR' for all Emacs commands which ultimately -;; use `shell-command' to asynchronously run some shell command. - -;; The command `with-editor-export-editor' exports `$EDITOR' or -;; another such environment variable in `shell-mode', `eshell-mode', -;; `term-mode' and `vterm-mode' buffers. Use this Emacs command -;; before executing a shell command which needs the editor set, or -;; always arrange for the current Emacs instance to be used as editor -;; by adding it to the appropriate mode hooks: -;; -;; (add-hook 'shell-mode-hook #'with-editor-export-editor) -;; (add-hook 'eshell-mode-hook #'with-editor-export-editor) -;; (add-hook 'term-exec-hook #'with-editor-export-editor) -;; (add-hook 'vterm-mode-hook #'with-editor-export-editor) - -;; Some variants of this function exist, these two forms are -;; equivalent: -;; -;; (add-hook 'shell-mode-hook -;; (apply-partially #'with-editor-export-editor "GIT_EDITOR")) -;; (add-hook 'shell-mode-hook #'with-editor-export-git-editor) - -;; This library can also be used by other packages which need to use -;; the current Emacs instance as editor. In fact this library was -;; written for Magit and its `git-commit-mode' and `git-rebase-mode'. -;; Consult `git-rebase.el' and the related code in `magit-sequence.el' -;; for a simple example. - -;;; Code: - -(require 'cl-lib) -(require 'compat) -(require 'server) -(require 'shell) -(eval-when-compile (require 'subr-x)) - -(declare-function dired-get-filename "dired" - (&optional localp no-error-if-not-filep)) -(declare-function term-emulate-terminal "term" (proc str)) -(declare-function vterm-send-return "vterm" ()) -(declare-function vterm-send-string "vterm" (string &optional paste-p)) -(defvar eshell-preoutput-filter-functions) -(defvar git-commit-post-finish-hook) -(defvar vterm--process) -(defvar warning-minimum-level) -(defvar warning-minimum-log-level) - -;;; Options - -(defgroup with-editor nil - "Use the Emacsclient as $EDITOR." - :group 'external - :group 'server) - -(defun with-editor-locate-emacsclient () - "Search for a suitable Emacsclient executable." - (or (with-editor-locate-emacsclient-1 - (with-editor-emacsclient-path) - (length (split-string emacs-version "\\."))) - (prog1 nil (display-warning 'with-editor "\ -Cannot determine a suitable Emacsclient - -Determining an Emacsclient executable suitable for the -current Emacs instance failed. For more information -please see https://github.com/magit/magit/wiki/Emacsclient.")))) - -(defun with-editor-locate-emacsclient-1 (path depth) - (let* ((version-lst (cl-subseq (split-string emacs-version "\\.") 0 depth)) - (version-reg (concat "^" (mapconcat #'identity version-lst "\\.")))) - (or (locate-file - (cond ((equal (downcase invocation-name) "remacs") - "remacsclient") - ((bound-and-true-p emacsclient-program-name)) - ("emacsclient")) - path - (cl-mapcan - (lambda (v) (cl-mapcar (lambda (e) (concat v e)) exec-suffixes)) - (nconc (and (boundp 'debian-emacs-flavor) - (list (format ".%s" debian-emacs-flavor))) - (cl-mapcon (lambda (v) - (setq v (mapconcat #'identity (reverse v) ".")) - (list v (concat "-" v) (concat ".emacs" v))) - (reverse version-lst)) - (list "" "-snapshot" ".emacs-snapshot"))) - (lambda (exec) - (ignore-errors - (string-match-p version-reg - (with-editor-emacsclient-version exec))))) - (and (> depth 1) - (with-editor-locate-emacsclient-1 path (1- depth)))))) - -(defun with-editor-emacsclient-version (exec) - (let ((default-directory (file-name-directory exec))) - (ignore-errors - (cadr (split-string (car (process-lines exec "--version"))))))) - -(defun with-editor-emacsclient-path () - (let ((path exec-path)) - (when invocation-directory - (push (directory-file-name invocation-directory) path) - (let* ((linkname (expand-file-name invocation-name invocation-directory)) - (truename (file-chase-links linkname))) - (unless (equal truename linkname) - (push (directory-file-name (file-name-directory truename)) path))) - (when (eq system-type 'darwin) - (let ((dir (expand-file-name "bin" invocation-directory))) - (when (file-directory-p dir) - (push dir path))) - (when (string-search "Cellar" invocation-directory) - (let ((dir (expand-file-name "../../../bin" invocation-directory))) - (when (file-directory-p dir) - (push dir path)))))) - (cl-remove-duplicates path :test #'equal))) - -(defcustom with-editor-emacsclient-executable (with-editor-locate-emacsclient) - "The Emacsclient executable used by the `with-editor' macro." - :group 'with-editor - :type '(choice (string :tag "Executable") - (const :tag "Don't use Emacsclient" nil))) - -(defcustom with-editor-sleeping-editor "\ -sh -c '\ -printf \"\\nWITH-EDITOR: $$ OPEN $0\\037$1\\037 IN $(pwd)\\n\"; \ -sleep 604800 & sleep=$!; \ -trap \"kill $sleep; exit 0\" USR1; \ -trap \"kill $sleep; exit 1\" USR2; \ -wait $sleep'" - "The sleeping editor, used when the Emacsclient cannot be used. - -This fallback is used for asynchronous processes started inside -the macro `with-editor', when the process runs on a remote machine -or for local processes when `with-editor-emacsclient-executable' -is nil (i.e., when no suitable Emacsclient was found, or the user -decided not to use it). - -Where the latter uses a socket to communicate with Emacs' server, -this substitute prints edit requests to its standard output on -which a process filter listens for such requests. As such it is -not a complete substitute for a proper Emacsclient, it can only -be used as $EDITOR of child process of the current Emacs instance. - -Some shells do not execute traps immediately when waiting for a -child process, but by default we do use such a blocking child -process. - -If you use such a shell (e.g., `csh' on FreeBSD, but not Debian), -then you have to edit this option. You can either replace \"sh\" -with \"bash\" (and install that), or you can use the older, less -performant implementation: - - \"sh -c '\\ - echo -e \\\"\\nWITH-EDITOR: $$ OPEN $0$1 IN $(pwd)\\n\\\"; \\ - trap \\\"exit 0\\\" USR1; \\ - trap \\\"exit 1\" USR2; \\ - while true; do sleep 1; done'\" - -Note that the two unit separator characters () right after $0 -and $1 are required. Normally $0 is the file name and $1 is -missing or else gets ignored. But if $0 has the form \"+N[:N]\", -then it is treated as a position in the file and $1 is expected -to be the file. - -Also note that using this alternative implementation leads to a -delay of up to a second. The delay can be shortened by replacing -\"sleep 1\" with \"sleep 0.01\", or if your implementation does -not support floats, then by using \"nanosleep\" instead." - :package-version '(with-editor . "2.8.0") - :group 'with-editor - :type 'string) - -(defcustom with-editor-finish-query-functions nil - "List of functions called to query before finishing session. - -The buffer in question is current while the functions are called. -If any of them returns nil, then the session is not finished and -the buffer is not killed. The user should then fix the issue and -try again. The functions are called with one argument. If it is -non-nil then that indicates that the user used a prefix argument -to force finishing the session despite issues. Functions should -usually honor that and return non-nil." - :group 'with-editor - :type 'hook) -(put 'with-editor-finish-query-functions 'permanent-local t) - -(defcustom with-editor-cancel-query-functions nil - "List of functions called to query before canceling session. - -The buffer in question is current while the functions are called. -If any of them returns nil, then the session is not canceled and -the buffer is not killed. The user should then fix the issue and -try again. The functions are called with one argument. If it is -non-nil then that indicates that the user used a prefix argument -to force canceling the session despite issues. Functions should -usually honor that and return non-nil." - :group 'with-editor - :type 'hook) -(put 'with-editor-cancel-query-functions 'permanent-local t) - -(defcustom with-editor-mode-lighter " WE" - "The mode-line lighter of the With-Editor mode." - :group 'with-editor - :type '(choice (const :tag "No lighter" "") string)) - -(defvar with-editor-server-window-alist nil - "Alist of filename patterns vs corresponding `server-window'. - -Each element looks like (REGEXP . FUNCTION). Files matching -REGEXP are selected using FUNCTION instead of the default in -`server-window'. - -Note that when a package adds an entry here then it probably -has a reason to disrespect `server-window' and it likely is -not a good idea to change such entries.") - -(defvar with-editor-file-name-history-exclude nil - "List of regexps for filenames `server-visit' should not remember. -When a filename matches any of the regexps, then `server-visit' -does not add it to the variable `file-name-history', which is -used when reading a filename in the minibuffer.") - -(defcustom with-editor-shell-command-use-emacsclient t - "Whether to use the emacsclient when running shell commands. - -This affects `with-editor-async-shell-command' and, if the input -ends with \"&\" `with-editor-shell-command' . - -If `shell-command-with-editor-mode' is enabled, then it also -affects `shell-command-async' and, if the input ends with \"&\" -`shell-command'. - -This is a temporary kludge that lets you choose between two -possible defects, the ones described in the issues #23 and #40. - -When t, then use the emacsclient. This has the disadvantage that -`with-editor-mode' won't be enabled because we don't know whether -this package was involved at all in the call to the emacsclient, -and when it is not, then we really should. The problem is that -the emacsclient doesn't pass along any environment variables to -the server. This will hopefully be fixed in Emacs eventually. - -When nil, then use the sleeping editor. Because in this case we -know that this package is involved, we can enable the mode. But -this makes it necessary that you invoke $EDITOR in shell scripts -like so: - - eval \"$EDITOR\" file - -And some tools that do not handle $EDITOR properly also break." - :package-version '(with-editor . "2.7.1") - :group 'with-editor - :type 'boolean) - -;;; Mode Commands - -(defvar with-editor-pre-finish-hook nil) -(defvar with-editor-pre-cancel-hook nil) -(defvar with-editor-post-finish-hook nil) -(defvar with-editor-post-finish-hook-1 nil) -(defvar with-editor-post-cancel-hook nil) -(defvar with-editor-post-cancel-hook-1 nil) -(defvar with-editor-cancel-alist nil) -(put 'with-editor-pre-finish-hook 'permanent-local t) -(put 'with-editor-pre-cancel-hook 'permanent-local t) -(put 'with-editor-post-finish-hook 'permanent-local t) -(put 'with-editor-post-cancel-hook 'permanent-local t) - -(defvar-local with-editor-show-usage t) -(defvar-local with-editor-cancel-message nil) -(defvar-local with-editor-previous-winconf nil) -(put 'with-editor-cancel-message 'permanent-local t) -(put 'with-editor-previous-winconf 'permanent-local t) - -(defvar-local with-editor--pid nil "For internal use.") -(put 'with-editor--pid 'permanent-local t) - -(defun with-editor-finish (force) - "Finish the current edit session." - (interactive "P") - (when (run-hook-with-args-until-failure - 'with-editor-finish-query-functions force) - (let ((post-finish-hook with-editor-post-finish-hook) - (post-commit-hook (bound-and-true-p git-commit-post-finish-hook)) - (dir default-directory)) - (run-hooks 'with-editor-pre-finish-hook) - (with-editor-return nil) - (accept-process-output nil 0.1) - (with-temp-buffer - (setq default-directory dir) - (setq-local with-editor-post-finish-hook post-finish-hook) - (when post-commit-hook - (setq-local git-commit-post-finish-hook post-commit-hook)) - (run-hooks 'with-editor-post-finish-hook))))) - -(defun with-editor-cancel (force) - "Cancel the current edit session." - (interactive "P") - (when (run-hook-with-args-until-failure - 'with-editor-cancel-query-functions force) - (let ((message with-editor-cancel-message)) - (when (functionp message) - (setq message (funcall message))) - (let ((post-cancel-hook with-editor-post-cancel-hook) - (with-editor-cancel-alist nil) - (dir default-directory)) - (run-hooks 'with-editor-pre-cancel-hook) - (with-editor-return t) - (accept-process-output nil 0.1) - (with-temp-buffer - (setq default-directory dir) - (setq-local with-editor-post-cancel-hook post-cancel-hook) - (run-hooks 'with-editor-post-cancel-hook))) - (message (or message "Canceled by user"))))) - -(defun with-editor-return (cancel) - (let ((winconf with-editor-previous-winconf) - (clients server-buffer-clients) - (dir default-directory) - (pid with-editor--pid)) - (remove-hook 'kill-buffer-query-functions - #'with-editor-kill-buffer-noop t) - (cond (cancel - (save-buffer) - (if clients - (let ((buf (current-buffer))) - (dolist (client clients) - (message "client %S" client) - (ignore-errors - (server-send-string client "-error Canceled by user")) - (delete-process client)) - (when (buffer-live-p buf) - (kill-buffer buf))) - ;; Fallback for when emacs was used as $EDITOR - ;; instead of emacsclient or the sleeping editor. - ;; See https://github.com/magit/magit/issues/2258. - (ignore-errors (delete-file buffer-file-name)) - (kill-buffer))) - (t - (save-buffer) - (if clients - ;; Don't use `server-edit' because we do not want to - ;; show another buffer belonging to another client. - ;; See https://github.com/magit/magit/issues/2197. - (server-done) - (kill-buffer)))) - (when pid - (let ((default-directory dir)) - (process-file "kill" nil nil nil - "-s" (if cancel "USR2" "USR1") pid))) - (when (and winconf (eq (window-configuration-frame winconf) - (selected-frame))) - (set-window-configuration winconf)))) - -;;; Mode - -(defvar-keymap with-editor-mode-map - "C-c C-c" #'with-editor-finish - "<remap> <server-edit>" #'with-editor-finish - "<remap> <evil-save-and-close>" #'with-editor-finish - "<remap> <evil-save-modified-and-close>" #'with-editor-finish - "C-c C-k" #'with-editor-cancel - "<remap> <kill-buffer>" #'with-editor-cancel - "<remap> <ido-kill-buffer>" #'with-editor-cancel - "<remap> <iswitchb-kill-buffer>" #'with-editor-cancel - "<remap> <evil-quit>" #'with-editor-cancel) - -(define-minor-mode with-editor-mode - "Edit a file as the $EDITOR of an external process." - :lighter with-editor-mode-lighter - ;; Protect the user from killing the buffer without using - ;; either `with-editor-finish' or `with-editor-cancel', - ;; and from removing the key bindings for these commands. - (unless with-editor-mode - (user-error "With-Editor mode cannot be turned off")) - (add-hook 'kill-buffer-query-functions - #'with-editor-kill-buffer-noop nil t) - ;; `server-execute' displays a message which is not - ;; correct when using this mode. - (when with-editor-show-usage - (with-editor-usage-message))) - -(put 'with-editor-mode 'permanent-local t) - -(defun with-editor-kill-buffer-noop () - ;; We started doing this in response to #64, but it is not safe - ;; to do so, because the client has already been killed, causing - ;; `with-editor-return' (called by `with-editor-cancel') to delete - ;; the file, see #66. The reason we delete the file in the first - ;; place are https://github.com/magit/magit/issues/2258 and - ;; https://github.com/magit/magit/issues/2248. - ;; (if (memq this-command '(save-buffers-kill-terminal - ;; save-buffers-kill-emacs)) - ;; (let ((with-editor-cancel-query-functions nil)) - ;; (with-editor-cancel nil) - ;; t) - ;; ...) - ;; So go back to always doing this instead: - (user-error (substitute-command-keys (format "\ -Don't kill this buffer %S. Instead cancel using \\[with-editor-cancel]" - (current-buffer))))) - -(defvar-local with-editor-usage-message "\ -Type \\[with-editor-finish] to finish, \ -or \\[with-editor-cancel] to cancel") - -(defun with-editor-usage-message () - ;; Run after `server-execute', which is run using - ;; a timer which starts immediately. - (let ((buffer (current-buffer))) - (run-with-timer - 0.05 nil - (lambda () - (with-current-buffer buffer - (message (substitute-command-keys with-editor-usage-message))))))) - -;;; Wrappers - -(defvar with-editor--envvar nil "For internal use.") - -(defmacro with-editor (&rest body) - "Use the Emacsclient as $EDITOR while evaluating BODY. -Modify the `process-environment' for processes started in BODY, -instructing them to use the Emacsclient as $EDITOR. If optional -ENVVAR is a literal string then bind that environment variable -instead. -\n(fn [ENVVAR] BODY...)" - (declare (indent defun) (debug (body))) - `(let ((with-editor--envvar ,(if (stringp (car body)) - (pop body) - '(or with-editor--envvar "EDITOR"))) - (process-environment process-environment)) - (with-editor--setup) - ,@body)) - -(defmacro with-editor* (envvar &rest body) - "Use the Emacsclient as the editor while evaluating BODY. -Modify the `process-environment' for processes started in BODY, -instructing them to use the Emacsclient as editor. ENVVAR is the -environment variable that is exported to do so, it is evaluated -at run-time. -\n(fn [ENVVAR] BODY...)" - (declare (indent defun) (debug (sexp body))) - `(let ((with-editor--envvar ,envvar) - (process-environment process-environment)) - (with-editor--setup) - ,@body)) - -(defun with-editor--setup () - (if (or (not with-editor-emacsclient-executable) - (file-remote-p default-directory)) - (push (concat with-editor--envvar "=" with-editor-sleeping-editor) - process-environment) - ;; Make sure server-use-tcp's value is valid. - (unless (featurep 'make-network-process '(:family local)) - (setq server-use-tcp t)) - ;; Make sure the server is running. - (unless (process-live-p server-process) - (when (server-running-p server-name) - (setq server-name (format "server%s" (emacs-pid))) - (when (server-running-p server-name) - (server-force-delete server-name))) - (server-start)) - ;; Tell $EDITOR to use the Emacsclient. - (push (concat with-editor--envvar "=" - ;; Quoting is the right thing to do. Applications that - ;; fail because of that, are the ones that need fixing, - ;; e.g., by using 'eval "$EDITOR" file'. See #121. - (shell-quote-argument - ;; If users set the executable manually, they might - ;; begin the path with "~", which would get quoted. - (if (string-prefix-p "~" with-editor-emacsclient-executable) - (concat (expand-file-name "~") - (substring with-editor-emacsclient-executable 1)) - with-editor-emacsclient-executable)) - ;; Tell the process where the server file is. - (and (not server-use-tcp) - (concat " --socket-name=" - (shell-quote-argument - (expand-file-name server-name - server-socket-dir))))) - process-environment) - (when server-use-tcp - (push (concat "EMACS_SERVER_FILE=" - (expand-file-name server-name server-auth-dir)) - process-environment)) - ;; As last resort fallback to the sleeping editor. - (push (concat "ALTERNATE_EDITOR=" with-editor-sleeping-editor) - process-environment))) - -(defun with-editor-server-window () - (or (and buffer-file-name - (cdr (cl-find-if (lambda (cons) - (string-match-p (car cons) buffer-file-name)) - with-editor-server-window-alist))) - server-window)) - -(defun server-switch-buffer--with-editor-server-window-alist - (fn &optional next-buffer &rest args) - "Honor `with-editor-server-window-alist' (which see)." - (let ((server-window (with-current-buffer - (or next-buffer (current-buffer)) - (when with-editor-mode - (setq with-editor-previous-winconf - (current-window-configuration))) - (with-editor-server-window)))) - (apply fn next-buffer args))) - -(advice-add 'server-switch-buffer :around - #'server-switch-buffer--with-editor-server-window-alist) - -(defun start-file-process--with-editor-process-filter - (fn name buffer program &rest program-args) - "When called inside a `with-editor' form and the Emacsclient -cannot be used, then give the process the filter function -`with-editor-process-filter'. To avoid overriding the filter -being added here you should use `with-editor-set-process-filter' -instead of `set-process-filter' inside `with-editor' forms. - -When the `default-directory' is located on a remote machine, -then also manipulate PROGRAM and PROGRAM-ARGS in order to set -the appropriate editor environment variable." - (if (not with-editor--envvar) - (apply fn name buffer program program-args) - (when (file-remote-p default-directory) - (unless (equal program "env") - (push program program-args) - (setq program "env")) - (push (concat with-editor--envvar "=" with-editor-sleeping-editor) - program-args)) - (let ((process (apply fn name buffer program program-args))) - (set-process-filter process #'with-editor-process-filter) - (process-put process 'default-dir default-directory) - process))) - -(advice-add 'start-file-process :around - #'start-file-process--with-editor-process-filter) - -(cl-defun make-process--with-editor-process-filter - (fn &rest keys &key name buffer command coding noquery stop - connection-type filter sentinel stderr file-handler - &allow-other-keys) - "When called inside a `with-editor' form and the Emacsclient -cannot be used, then give the process the filter function -`with-editor-process-filter'. To avoid overriding the filter -being added here you should use `with-editor-set-process-filter' -instead of `set-process-filter' inside `with-editor' forms. - -When the `default-directory' is located on a remote machine and -FILE-HANDLER is non-nil, then also manipulate COMMAND in order -to set the appropriate editor environment variable." - (if (or (not file-handler) (not with-editor--envvar)) - (apply fn keys) - (when (file-remote-p default-directory) - (unless (equal (car command) "env") - (push "env" command)) - (push (concat with-editor--envvar "=" with-editor-sleeping-editor) - (cdr command))) - (let* ((filter (if filter - (lambda (process output) - (funcall filter process output) - (with-editor-process-filter process output t)) - #'with-editor-process-filter)) - (process (funcall fn - :name name - :buffer buffer - :command command - :coding coding - :noquery noquery - :stop stop - :connection-type connection-type - :filter filter - :sentinel sentinel - :stderr stderr - :file-handler file-handler))) - (process-put process 'default-dir default-directory) - process))) - -(advice-add #'make-process :around #'make-process--with-editor-process-filter) - -(defun with-editor-set-process-filter (process filter) - "Like `set-process-filter' but keep `with-editor-process-filter'. -Give PROCESS the new FILTER but keep `with-editor-process-filter' -if that was added earlier by the advised `start-file-process'. - -Do so by wrapping the two filter functions using a lambda, which -becomes the actual filter. It calls FILTER first, which may or -may not insert the text into the PROCESS's buffer. Then it calls -`with-editor-process-filter', passing t as NO-STANDARD-FILTER." - (set-process-filter - process - (if (eq (process-filter process) 'with-editor-process-filter) - `(lambda (proc str) - (,filter proc str) - (with-editor-process-filter proc str t)) - filter))) - -(defvar with-editor-filter-visit-hook nil) - -(defconst with-editor-sleeping-editor-regexp "^\ -WITH-EDITOR: \\([0-9]+\\) \ -OPEN \\([^]+?\\)\ -\\(?:\\([^]*\\)\\)?\ -\\(?: IN \\([^\r]+?\\)\\)?\r?$") - -(defvar with-editor--max-incomplete-length 1000) - -(defun with-editor-sleeping-editor-filter (process string) - (when-let ((incomplete (and process (process-get process 'incomplete)))) - (setq string (concat incomplete string))) - (save-match-data - (cond - ((and process (not (string-suffix-p "\n" string))) - (let ((length (length string))) - (when (> length with-editor--max-incomplete-length) - (setq string - (substring string - (- length with-editor--max-incomplete-length))))) - (process-put process 'incomplete string) - nil) - ((string-match with-editor-sleeping-editor-regexp string) - (when process - (process-put process 'incomplete nil)) - (let ((pid (match-string 1 string)) - (arg0 (match-string 2 string)) - (arg1 (match-string 3 string)) - (dir (match-string 4 string)) - file line column) - (cond ((string-match "\\`\\+\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?\\'" arg0) - (setq file arg1) - (setq line (string-to-number (match-string 1 arg0))) - (setq column (match-string 2 arg0)) - (setq column (and column (string-to-number column)))) - ((setq file arg0))) - (unless (file-name-absolute-p file) - (setq file (expand-file-name file dir))) - (when default-directory - (setq file (concat (file-remote-p default-directory) file))) - (with-current-buffer (find-file-noselect file) - (with-editor-mode 1) - (setq with-editor--pid pid) - (setq with-editor-previous-winconf - (current-window-configuration)) - (when line - (let ((pos (save-excursion - (save-restriction - (goto-char (point-min)) - (forward-line (1- line)) - (when column - (move-to-column column)) - (point))))) - (when (and (buffer-narrowed-p) - widen-automatically - (not (<= (point-min) pos (point-max)))) - (widen)) - (goto-char pos))) - (run-hooks 'with-editor-filter-visit-hook) - (funcall (or (with-editor-server-window) #'switch-to-buffer) - (current-buffer)) - (kill-local-variable 'server-window))) - nil) - (t string)))) - -(defun with-editor-process-filter - (process string &optional no-default-filter) - "Listen for edit requests by child processes." - (let ((default-directory (process-get process 'default-dir))) - (with-editor-sleeping-editor-filter process string)) - (unless no-default-filter - (internal-default-process-filter process string))) - -(advice-add 'server-visit-files :after - #'server-visit-files--with-editor-file-name-history-exclude) - -(defun server-visit-files--with-editor-file-name-history-exclude - (files _proc &optional _nowait) - (pcase-dolist (`(,file . ,_) files) - (when (cl-find-if (lambda (regexp) - (string-match-p regexp file)) - with-editor-file-name-history-exclude) - (setq file-name-history (delete file file-name-history))))) - -;;; Augmentations - -;;;###autoload -(cl-defun with-editor-export-editor (&optional (envvar "EDITOR")) - "Teach subsequent commands to use current Emacs instance as editor. - -Set and export the environment variable ENVVAR, by default -\"EDITOR\". The value is automatically generated to teach -commands to use the current Emacs instance as \"the editor\". - -This works in `shell-mode', `term-mode', `eshell-mode' and -`vterm'." - (interactive (list (with-editor-read-envvar))) - (cond - ((derived-mode-p 'comint-mode 'term-mode) - (when-let ((process (get-buffer-process (current-buffer)))) - (goto-char (process-mark process)) - (process-send-string - process (format " export %s=%s\n" envvar - (shell-quote-argument with-editor-sleeping-editor))) - (while (accept-process-output process 0.1)) - (if (derived-mode-p 'term-mode) - (with-editor-set-process-filter process #'with-editor-emulate-terminal) - (add-hook 'comint-output-filter-functions #'with-editor-output-filter - nil t)))) - ((derived-mode-p 'eshell-mode) - (add-to-list 'eshell-preoutput-filter-functions - #'with-editor-output-filter) - (setenv envvar with-editor-sleeping-editor)) - ((derived-mode-p 'vterm-mode) - (if with-editor-emacsclient-executable - (let ((with-editor--envvar envvar) - (process-environment process-environment)) - (with-editor--setup) - (while (accept-process-output vterm--process 0.1)) - (when-let ((v (getenv envvar))) - (vterm-send-string (format " export %s=%S" envvar v)) - (vterm-send-return)) - (when-let ((v (getenv "EMACS_SERVER_FILE"))) - (vterm-send-string (format " export EMACS_SERVER_FILE=%S" v)) - (vterm-send-return)) - (vterm-send-string "clear") - (vterm-send-return)) - (error "Cannot use sleeping editor in this buffer"))) - (t - (error "Cannot export environment variables in this buffer"))) - (message "Successfully exported %s" envvar)) - -;;;###autoload -(defun with-editor-export-git-editor () - "Like `with-editor-export-editor' but always set `$GIT_EDITOR'." - (interactive) - (with-editor-export-editor "GIT_EDITOR")) - -;;;###autoload -(defun with-editor-export-hg-editor () - "Like `with-editor-export-editor' but always set `$HG_EDITOR'." - (interactive) - (with-editor-export-editor "HG_EDITOR")) - -(defun with-editor-output-filter (string) - "Handle edit requests on behalf of `comint-mode' and `eshell-mode'." - (with-editor-sleeping-editor-filter nil string)) - -(defun with-editor-emulate-terminal (process string) - "Like `term-emulate-terminal' but also handle edit requests." - (let ((with-editor-sleeping-editor-regexp - (substring with-editor-sleeping-editor-regexp 1))) - (with-editor-sleeping-editor-filter process string)) - (term-emulate-terminal process string)) - -(defvar with-editor-envvars '("EDITOR" "GIT_EDITOR" "HG_EDITOR")) - -(cl-defun with-editor-read-envvar - (&optional (prompt "Set environment variable") - (default "EDITOR")) - (let ((reply (completing-read (if default - (format "%s (%s): " prompt default) - (concat prompt ": ")) - with-editor-envvars nil nil nil nil default))) - (if (string= reply "") (user-error "Nothing selected") reply))) - -;;;###autoload -(define-minor-mode shell-command-with-editor-mode - "Teach `shell-command' to use current Emacs instance as editor. - -Teach `shell-command', and all commands that ultimately call that -command, to use the current Emacs instance as editor by executing -\"EDITOR=CLIENT COMMAND&\" instead of just \"COMMAND&\". - -CLIENT is automatically generated; EDITOR=CLIENT instructs -COMMAND to use to the current Emacs instance as \"the editor\", -assuming no other variable overrides the effect of \"$EDITOR\". -CLIENT may be the path to an appropriate emacsclient executable -with arguments, or a script which also works over Tramp. - -Alternatively you can use the `with-editor-async-shell-command', -which also allows the use of another variable instead of -\"EDITOR\"." - :global t) - -;;;###autoload -(defun with-editor-async-shell-command - (command &optional output-buffer error-buffer envvar) - "Like `async-shell-command' but with `$EDITOR' set. - -Execute string \"ENVVAR=CLIENT COMMAND\" in an inferior shell; -display output, if any. With a prefix argument prompt for an -environment variable, otherwise the default \"EDITOR\" variable -is used. With a negative prefix argument additionally insert -the COMMAND's output at point. - -CLIENT is automatically generated; ENVVAR=CLIENT instructs -COMMAND to use to the current Emacs instance as \"the editor\", -assuming it respects ENVVAR as an \"EDITOR\"-like variable. -CLIENT may be the path to an appropriate emacsclient executable -with arguments, or a script which also works over Tramp. - -Also see `async-shell-command' and `shell-command'." - (interactive (with-editor-shell-command-read-args "Async shell command: " t)) - (let ((with-editor--envvar envvar)) - (with-editor - (async-shell-command command output-buffer error-buffer)))) - -;;;###autoload -(defun with-editor-shell-command - (command &optional output-buffer error-buffer envvar) - "Like `shell-command' or `with-editor-async-shell-command'. -If COMMAND ends with \"&\" behave like the latter, -else like the former." - (interactive (with-editor-shell-command-read-args "Shell command: ")) - (if (string-match "&[ \t]*\\'" command) - (with-editor-async-shell-command - command output-buffer error-buffer envvar) - (shell-command command output-buffer error-buffer))) - -(defun with-editor-shell-command-read-args (prompt &optional async) - (let ((command (read-shell-command - prompt nil nil - (let ((filename (or buffer-file-name - (and (eq major-mode 'dired-mode) - (dired-get-filename nil t))))) - (and filename (file-relative-name filename)))))) - (list command - (if (or async (setq async (string-match-p "&[ \t]*\\'" command))) - (< (prefix-numeric-value current-prefix-arg) 0) - current-prefix-arg) - shell-command-default-error-buffer - (and async current-prefix-arg (with-editor-read-envvar))))) - -(defun shell-command--shell-command-with-editor-mode - (fn command &optional output-buffer error-buffer) - ;; `shell-mode' and its hook are intended for buffers in which an - ;; interactive shell is running, but `shell-command' also turns on - ;; that mode, even though it only runs the shell to run a single - ;; command. The `with-editor-export-editor' hook function is only - ;; intended to be used in buffers in which an interactive shell is - ;; running, so it has to be removed here. - (let ((shell-mode-hook (remove 'with-editor-export-editor shell-mode-hook))) - (cond ((or (not (or with-editor--envvar shell-command-with-editor-mode)) - (not (string-suffix-p "&" command))) - (funcall fn command output-buffer error-buffer)) - ((and with-editor-shell-command-use-emacsclient - with-editor-emacsclient-executable - (not (file-remote-p default-directory))) - (with-editor (funcall fn command output-buffer error-buffer))) - (t - (funcall fn (format "%s=%s %s" - (or with-editor--envvar "EDITOR") - (shell-quote-argument with-editor-sleeping-editor) - command) - output-buffer error-buffer) - (ignore-errors - (let ((process (get-buffer-process - (or output-buffer - (get-buffer "*Async Shell Command*"))))) - (set-process-filter - process (lambda (proc str) - (comint-output-filter proc str) - (with-editor-process-filter proc str t))) - process)))))) - -(advice-add 'shell-command :around - #'shell-command--shell-command-with-editor-mode) - -;;; _ - -(defun with-editor-debug () - "Debug configuration issues. -See info node `(with-editor)Debugging' for instructions." - (interactive) - (require 'warnings) - (with-current-buffer (get-buffer-create "*with-editor-debug*") - (pop-to-buffer (current-buffer)) - (erase-buffer) - (ignore-errors (with-editor)) - (insert - (format "with-editor: %s\n" (locate-library "with-editor.el")) - (format "emacs: %s (%s)\n" - (expand-file-name invocation-name invocation-directory) - emacs-version) - "system:\n" - (format " system-type: %s\n" system-type) - (format " system-configuration: %s\n" system-configuration) - (format " system-configuration-options: %s\n" system-configuration-options) - "server:\n" - (format " server-running-p: %s\n" (server-running-p)) - (format " server-process: %S\n" server-process) - (format " server-use-tcp: %s\n" server-use-tcp) - (format " server-name: %s\n" server-name) - (format " server-socket-dir: %s\n" server-socket-dir)) - (if (and server-socket-dir (file-accessible-directory-p server-socket-dir)) - (dolist (file (directory-files server-socket-dir nil "^[^.]")) - (insert (format " %s\n" file))) - (insert (format " %s: not an accessible directory\n" - (if server-use-tcp "WARNING" "ERROR")))) - (insert (format " server-auth-dir: %s\n" server-auth-dir)) - (if (file-accessible-directory-p server-auth-dir) - (dolist (file (directory-files server-auth-dir nil "^[^.]")) - (insert (format " %s\n" file))) - (insert (format " %s: not an accessible directory\n" - (if server-use-tcp "ERROR" "WARNING")))) - (let ((val with-editor-emacsclient-executable) - (def (default-value 'with-editor-emacsclient-executable)) - (fun (let ((warning-minimum-level :error) - (warning-minimum-log-level :error)) - (with-editor-locate-emacsclient)))) - (insert "with-editor-emacsclient-executable:\n" - (format " value: %s (%s)\n" val - (and val (with-editor-emacsclient-version val))) - (format " default: %s (%s)\n" def - (and def (with-editor-emacsclient-version def))) - (format " funcall: %s (%s)\n" fun - (and fun (with-editor-emacsclient-version fun))))) - (insert "path:\n" - (format " $PATH: %s\n" (split-string (getenv "PATH") ":")) - (format " exec-path: %s\n" exec-path)) - (insert (format " with-editor-emacsclient-path:\n")) - (dolist (dir (with-editor-emacsclient-path)) - (insert (format " %s (%s)\n" dir (car (file-attributes dir)))) - (when (file-directory-p dir) - ;; Don't match emacsclientw.exe, it makes popup windows. - (dolist (exec (directory-files dir t "emacsclient\\(?:[^w]\\|\\'\\)")) - (insert (format " %s (%s)\n" exec - (with-editor-emacsclient-version exec)))))))) - -(defconst with-editor-font-lock-keywords - '(("(\\(with-\\(?:git-\\)?editor\\)\\_>" (1 'font-lock-keyword-face)))) -(font-lock-add-keywords 'emacs-lisp-mode with-editor-font-lock-keywords) - -(provide 'with-editor) -;; Local Variables: -;; indent-tabs-mode: nil -;; End: -;;; with-editor.el ends here diff --git a/emacs/elpa/with-editor-20240415.1558/with-editor.elc b/emacs/elpa/with-editor-20240415.1558/with-editor.elc Binary files differ. diff --git a/emacs/elpa/with-editor-20240415.1558/dir b/emacs/elpa/with-editor-20240609.1518/dir diff --git a/emacs/elpa/with-editor-20240609.1518/with-editor-autoloads.el b/emacs/elpa/with-editor-20240609.1518/with-editor-autoloads.el @@ -0,0 +1,105 @@ +;;; with-editor-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- +;; Generated by the `loaddefs-generate' function. + +;; This file is part of GNU Emacs. + +;;; Code: + +(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) + + + +;;; Generated autoloads from with-editor.el + +(autoload 'with-editor-export-editor "with-editor" "\ +Teach subsequent commands to use current Emacs instance as editor. + +Set and export the environment variable ENVVAR, by default +\"EDITOR\". The value is automatically generated to teach +commands to use the current Emacs instance as \"the editor\". + +This works in `shell-mode', `term-mode', `eshell-mode' and +`vterm'. + +(fn &optional (ENVVAR \"EDITOR\"))" t) +(autoload 'with-editor-export-git-editor "with-editor" "\ +Like `with-editor-export-editor' but always set `$GIT_EDITOR'." t) +(autoload 'with-editor-export-hg-editor "with-editor" "\ +Like `with-editor-export-editor' but always set `$HG_EDITOR'." t) +(defvar shell-command-with-editor-mode nil "\ +Non-nil if Shell-Command-With-Editor mode is enabled. +See the `shell-command-with-editor-mode' command +for a description of this minor mode.") +(custom-autoload 'shell-command-with-editor-mode "with-editor" nil) +(autoload 'shell-command-with-editor-mode "with-editor" "\ +Teach `shell-command' to use current Emacs instance as editor. + +Teach `shell-command', and all commands that ultimately call that +command, to use the current Emacs instance as editor by executing +\"EDITOR=CLIENT COMMAND&\" instead of just \"COMMAND&\". + +CLIENT is automatically generated; EDITOR=CLIENT instructs +COMMAND to use to the current Emacs instance as \"the editor\", +assuming no other variable overrides the effect of \"$EDITOR\". +CLIENT may be the path to an appropriate emacsclient executable +with arguments, or a script which also works over Tramp. + +Alternatively you can use the `with-editor-async-shell-command', +which also allows the use of another variable instead of +\"EDITOR\". + +This is a global minor mode. If called interactively, toggle the +`Shell-Command-With-Editor mode' mode. If the prefix argument is +positive, enable the mode, and if it is zero or negative, disable +the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable +the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `(default-value \\='shell-command-with-editor-mode)'. + +The mode's hook is called both when the mode is enabled and when +it is disabled. + +(fn &optional ARG)" t) +(autoload 'with-editor-async-shell-command "with-editor" "\ +Like `async-shell-command' but with `$EDITOR' set. + +Execute string \"ENVVAR=CLIENT COMMAND\" in an inferior shell; +display output, if any. With a prefix argument prompt for an +environment variable, otherwise the default \"EDITOR\" variable +is used. With a negative prefix argument additionally insert +the COMMAND's output at point. + +CLIENT is automatically generated; ENVVAR=CLIENT instructs +COMMAND to use to the current Emacs instance as \"the editor\", +assuming it respects ENVVAR as an \"EDITOR\"-like variable. +CLIENT may be the path to an appropriate emacsclient executable +with arguments, or a script which also works over Tramp. + +Also see `async-shell-command' and `shell-command'. + +(fn COMMAND &optional OUTPUT-BUFFER ERROR-BUFFER ENVVAR)" t) +(autoload 'with-editor-shell-command "with-editor" "\ +Like `shell-command' or `with-editor-async-shell-command'. +If COMMAND ends with \"&\" behave like the latter, +else like the former. + +(fn COMMAND &optional OUTPUT-BUFFER ERROR-BUFFER ENVVAR)" t) +(register-definition-prefixes "with-editor" '("server-" "shell-command" "start-file-process" "with-editor")) + +;;; End of scraped data + +(provide 'with-editor-autoloads) + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; no-native-compile: t +;; coding: utf-8-emacs-unix +;; End: + +;;; with-editor-autoloads.el ends here diff --git a/emacs/elpa/with-editor-20240609.1518/with-editor-pkg.el b/emacs/elpa/with-editor-20240609.1518/with-editor-pkg.el @@ -0,0 +1,15 @@ +(define-package "with-editor" "20240609.1518" "Use the Emacsclient as $EDITOR" + '((emacs "25.1") + (compat "29.1.4.1")) + :commit "f6a3fc8f6735fbc804e02f9c54bc621746afd5b0" :authors + '(("Jonas Bernoulli" . "emacs.with-editor@jonas.bernoulli.dev")) + :maintainers + '(("Jonas Bernoulli" . "emacs.with-editor@jonas.bernoulli.dev")) + :maintainer + '("Jonas Bernoulli" . "emacs.with-editor@jonas.bernoulli.dev") + :keywords + '("processes" "terminals") + :url "https://github.com/magit/with-editor") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs/elpa/with-editor-20240609.1518/with-editor.el b/emacs/elpa/with-editor-20240609.1518/with-editor.el @@ -0,0 +1,980 @@ +;;; with-editor.el --- Use the Emacsclient as $EDITOR -*- lexical-binding:t -*- + +;; Copyright (C) 2014-2024 The Magit Project Contributors + +;; Author: Jonas Bernoulli <emacs.with-editor@jonas.bernoulli.dev> +;; Homepage: https://github.com/magit/with-editor +;; Keywords: processes terminals + +;; Package-Version: 3.3.2 +;; Package-Requires: ((emacs "25.1") (compat "29.1.4.1")) + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; This file is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation, either version 3 of the License, +;; or (at your option) any later version. +;; +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this file. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This library makes it possible to reliably use the Emacsclient as +;; the `$EDITOR' of child processes. It makes sure that they know how +;; to call home. For remote processes a substitute is provided, which +;; communicates with Emacs on standard output/input instead of using a +;; socket as the Emacsclient does. + +;; It provides the commands `with-editor-async-shell-command' and +;; `with-editor-shell-command', which are intended as replacements +;; for `async-shell-command' and `shell-command'. They automatically +;; export `$EDITOR' making sure the executed command uses the current +;; Emacs instance as "the editor". With a prefix argument these +;; commands prompt for an alternative environment variable such as +;; `$GIT_EDITOR'. To always use these variants add this to your init +;; file: +;; +;; (keymap-global-set "<remap> <async-shell-command>" +;; #'with-editor-async-shell-command) +;; (keymap-global-set "<remap> <shell-command>" +;; #'with-editor-shell-command) + +;; Alternatively use the global `shell-command-with-editor-mode', +;; which always sets `$EDITOR' for all Emacs commands which ultimately +;; use `shell-command' to asynchronously run some shell command. + +;; The command `with-editor-export-editor' exports `$EDITOR' or +;; another such environment variable in `shell-mode', `eshell-mode', +;; `term-mode' and `vterm-mode' buffers. Use this Emacs command +;; before executing a shell command which needs the editor set, or +;; always arrange for the current Emacs instance to be used as editor +;; by adding it to the appropriate mode hooks: +;; +;; (add-hook 'shell-mode-hook #'with-editor-export-editor) +;; (add-hook 'eshell-mode-hook #'with-editor-export-editor) +;; (add-hook 'term-exec-hook #'with-editor-export-editor) +;; (add-hook 'vterm-mode-hook #'with-editor-export-editor) + +;; Some variants of this function exist, these two forms are +;; equivalent: +;; +;; (add-hook 'shell-mode-hook +;; (apply-partially #'with-editor-export-editor "GIT_EDITOR")) +;; (add-hook 'shell-mode-hook #'with-editor-export-git-editor) + +;; This library can also be used by other packages which need to use +;; the current Emacs instance as editor. In fact this library was +;; written for Magit and its `git-commit-mode' and `git-rebase-mode'. +;; Consult `git-rebase.el' and the related code in `magit-sequence.el' +;; for a simple example. + +;;; Code: + +(require 'cl-lib) +(require 'compat) +(require 'server) +(require 'shell) +(eval-when-compile (require 'subr-x)) + +(declare-function dired-get-filename "dired" + (&optional localp no-error-if-not-filep)) +(declare-function term-emulate-terminal "term" (proc str)) +(defvar eshell-preoutput-filter-functions) +(defvar git-commit-post-finish-hook) +(defvar vterm--process) +(defvar warning-minimum-level) +(defvar warning-minimum-log-level) + +;;; Options + +(defgroup with-editor nil + "Use the Emacsclient as $EDITOR." + :group 'external + :group 'server) + +(defun with-editor-locate-emacsclient () + "Search for a suitable Emacsclient executable." + (or (with-editor-locate-emacsclient-1 + (with-editor-emacsclient-path) + (length (split-string emacs-version "\\."))) + (prog1 nil (display-warning 'with-editor "\ +Cannot determine a suitable Emacsclient + +Determining an Emacsclient executable suitable for the +current Emacs instance failed. For more information +please see https://github.com/magit/magit/wiki/Emacsclient.")))) + +(defun with-editor-locate-emacsclient-1 (path depth) + (let* ((version-lst (cl-subseq (split-string emacs-version "\\.") 0 depth)) + (version-reg (concat "^" (mapconcat #'identity version-lst "\\.")))) + (or (locate-file + (cond ((equal (downcase invocation-name) "remacs") + "remacsclient") + ((bound-and-true-p emacsclient-program-name)) + ("emacsclient")) + path + (cl-mapcan + (lambda (v) (cl-mapcar (lambda (e) (concat v e)) exec-suffixes)) + (nconc (and (boundp 'debian-emacs-flavor) + (list (format ".%s" debian-emacs-flavor))) + (cl-mapcon (lambda (v) + (setq v (mapconcat #'identity (reverse v) ".")) + (list v (concat "-" v) (concat ".emacs" v))) + (reverse version-lst)) + (list "" "-snapshot" ".emacs-snapshot"))) + (lambda (exec) + (ignore-errors + (string-match-p version-reg + (with-editor-emacsclient-version exec))))) + (and (> depth 1) + (with-editor-locate-emacsclient-1 path (1- depth)))))) + +(defun with-editor-emacsclient-version (exec) + (let ((default-directory (file-name-directory exec))) + (ignore-errors + (cadr (split-string (car (process-lines exec "--version"))))))) + +(defun with-editor-emacsclient-path () + (let ((path exec-path)) + (when invocation-directory + (push (directory-file-name invocation-directory) path) + (let* ((linkname (expand-file-name invocation-name invocation-directory)) + (truename (file-chase-links linkname))) + (unless (equal truename linkname) + (push (directory-file-name (file-name-directory truename)) path))) + (when (eq system-type 'darwin) + (let ((dir (expand-file-name "bin" invocation-directory))) + (when (file-directory-p dir) + (push dir path))) + (when (string-search "Cellar" invocation-directory) + (let ((dir (expand-file-name "../../../bin" invocation-directory))) + (when (file-directory-p dir) + (push dir path)))))) + (cl-remove-duplicates path :test #'equal))) + +(defcustom with-editor-emacsclient-executable (with-editor-locate-emacsclient) + "The Emacsclient executable used by the `with-editor' macro." + :group 'with-editor + :type '(choice (string :tag "Executable") + (const :tag "Don't use Emacsclient" nil))) + +(defcustom with-editor-sleeping-editor "\ +sh -c '\ +printf \"\\nWITH-EDITOR: $$ OPEN $0\\037$1\\037 IN $(pwd)\\n\"; \ +sleep 604800 & sleep=$!; \ +trap \"kill $sleep; exit 0\" USR1; \ +trap \"kill $sleep; exit 1\" USR2; \ +wait $sleep'" + "The sleeping editor, used when the Emacsclient cannot be used. + +This fallback is used for asynchronous processes started inside +the macro `with-editor', when the process runs on a remote machine +or for local processes when `with-editor-emacsclient-executable' +is nil (i.e., when no suitable Emacsclient was found, or the user +decided not to use it). + +Where the latter uses a socket to communicate with Emacs' server, +this substitute prints edit requests to its standard output on +which a process filter listens for such requests. As such it is +not a complete substitute for a proper Emacsclient, it can only +be used as $EDITOR of child process of the current Emacs instance. + +Some shells do not execute traps immediately when waiting for a +child process, but by default we do use such a blocking child +process. + +If you use such a shell (e.g., `csh' on FreeBSD, but not Debian), +then you have to edit this option. You can either replace \"sh\" +with \"bash\" (and install that), or you can use the older, less +performant implementation: + + \"sh -c '\\ + echo -e \\\"\\nWITH-EDITOR: $$ OPEN $0$1 IN $(pwd)\\n\\\"; \\ + trap \\\"exit 0\\\" USR1; \\ + trap \\\"exit 1\" USR2; \\ + while true; do sleep 1; done'\" + +Note that the two unit separator characters () right after $0 +and $1 are required. Normally $0 is the file name and $1 is +missing or else gets ignored. But if $0 has the form \"+N[:N]\", +then it is treated as a position in the file and $1 is expected +to be the file. + +Also note that using this alternative implementation leads to a +delay of up to a second. The delay can be shortened by replacing +\"sleep 1\" with \"sleep 0.01\", or if your implementation does +not support floats, then by using \"nanosleep\" instead." + :package-version '(with-editor . "2.8.0") + :group 'with-editor + :type 'string) + +(defcustom with-editor-finish-query-functions nil + "List of functions called to query before finishing session. + +The buffer in question is current while the functions are called. +If any of them returns nil, then the session is not finished and +the buffer is not killed. The user should then fix the issue and +try again. The functions are called with one argument. If it is +non-nil then that indicates that the user used a prefix argument +to force finishing the session despite issues. Functions should +usually honor that and return non-nil." + :group 'with-editor + :type 'hook) +(put 'with-editor-finish-query-functions 'permanent-local t) + +(defcustom with-editor-cancel-query-functions nil + "List of functions called to query before canceling session. + +The buffer in question is current while the functions are called. +If any of them returns nil, then the session is not canceled and +the buffer is not killed. The user should then fix the issue and +try again. The functions are called with one argument. If it is +non-nil then that indicates that the user used a prefix argument +to force canceling the session despite issues. Functions should +usually honor that and return non-nil." + :group 'with-editor + :type 'hook) +(put 'with-editor-cancel-query-functions 'permanent-local t) + +(defcustom with-editor-mode-lighter " WE" + "The mode-line lighter of the With-Editor mode." + :group 'with-editor + :type '(choice (const :tag "No lighter" "") string)) + +(defvar with-editor-server-window-alist nil + "Alist of filename patterns vs corresponding `server-window'. + +Each element looks like (REGEXP . FUNCTION). Files matching +REGEXP are selected using FUNCTION instead of the default in +`server-window'. + +Note that when a package adds an entry here then it probably +has a reason to disrespect `server-window' and it likely is +not a good idea to change such entries.") + +(defvar with-editor-file-name-history-exclude nil + "List of regexps for filenames `server-visit' should not remember. +When a filename matches any of the regexps, then `server-visit' +does not add it to the variable `file-name-history', which is +used when reading a filename in the minibuffer.") + +(defcustom with-editor-shell-command-use-emacsclient t + "Whether to use the emacsclient when running shell commands. + +This affects `with-editor-async-shell-command' and, if the input +ends with \"&\" `with-editor-shell-command' . + +If `shell-command-with-editor-mode' is enabled, then it also +affects `shell-command-async' and, if the input ends with \"&\" +`shell-command'. + +This is a temporary kludge that lets you choose between two +possible defects, the ones described in the issues #23 and #40. + +When t, then use the emacsclient. This has the disadvantage that +`with-editor-mode' won't be enabled because we don't know whether +this package was involved at all in the call to the emacsclient, +and when it is not, then we really should. The problem is that +the emacsclient doesn't pass along any environment variables to +the server. This will hopefully be fixed in Emacs eventually. + +When nil, then use the sleeping editor. Because in this case we +know that this package is involved, we can enable the mode. But +this makes it necessary that you invoke $EDITOR in shell scripts +like so: + + eval \"$EDITOR\" file + +And some tools that do not handle $EDITOR properly also break." + :package-version '(with-editor . "2.7.1") + :group 'with-editor + :type 'boolean) + +;;; Mode Commands + +(defvar with-editor-pre-finish-hook nil) +(defvar with-editor-pre-cancel-hook nil) +(defvar with-editor-post-finish-hook nil) +(defvar with-editor-post-finish-hook-1 nil) +(defvar with-editor-post-cancel-hook nil) +(defvar with-editor-post-cancel-hook-1 nil) +(defvar with-editor-cancel-alist nil) +(put 'with-editor-pre-finish-hook 'permanent-local t) +(put 'with-editor-pre-cancel-hook 'permanent-local t) +(put 'with-editor-post-finish-hook 'permanent-local t) +(put 'with-editor-post-cancel-hook 'permanent-local t) + +(defvar-local with-editor-show-usage t) +(defvar-local with-editor-cancel-message nil) +(defvar-local with-editor-previous-winconf nil) +(put 'with-editor-cancel-message 'permanent-local t) +(put 'with-editor-previous-winconf 'permanent-local t) + +(defvar-local with-editor--pid nil "For internal use.") +(put 'with-editor--pid 'permanent-local t) + +(defun with-editor-finish (force) + "Finish the current edit session." + (interactive "P") + (when (run-hook-with-args-until-failure + 'with-editor-finish-query-functions force) + (let ((post-finish-hook with-editor-post-finish-hook) + (post-commit-hook (bound-and-true-p git-commit-post-finish-hook)) + (dir default-directory)) + (run-hooks 'with-editor-pre-finish-hook) + (with-editor-return nil) + (accept-process-output nil 0.1) + (with-temp-buffer + (setq default-directory dir) + (setq-local with-editor-post-finish-hook post-finish-hook) + (when post-commit-hook + (setq-local git-commit-post-finish-hook post-commit-hook)) + (run-hooks 'with-editor-post-finish-hook))))) + +(defun with-editor-cancel (force) + "Cancel the current edit session." + (interactive "P") + (when (run-hook-with-args-until-failure + 'with-editor-cancel-query-functions force) + (let ((message with-editor-cancel-message)) + (when (functionp message) + (setq message (funcall message))) + (let ((post-cancel-hook with-editor-post-cancel-hook) + (with-editor-cancel-alist nil) + (dir default-directory)) + (run-hooks 'with-editor-pre-cancel-hook) + (with-editor-return t) + (accept-process-output nil 0.1) + (with-temp-buffer + (setq default-directory dir) + (setq-local with-editor-post-cancel-hook post-cancel-hook) + (run-hooks 'with-editor-post-cancel-hook))) + (message (or message "Canceled by user"))))) + +(defun with-editor-return (cancel) + (let ((winconf with-editor-previous-winconf) + (clients server-buffer-clients) + (dir default-directory) + (pid with-editor--pid)) + (remove-hook 'kill-buffer-query-functions + #'with-editor-kill-buffer-noop t) + (cond (cancel + (save-buffer) + (if clients + (let ((buf (current-buffer))) + (dolist (client clients) + (message "client %S" client) + (ignore-errors + (server-send-string client "-error Canceled by user")) + (delete-process client)) + (when (buffer-live-p buf) + (kill-buffer buf))) + ;; Fallback for when emacs was used as $EDITOR + ;; instead of emacsclient or the sleeping editor. + ;; See https://github.com/magit/magit/issues/2258. + (ignore-errors (delete-file buffer-file-name)) + (kill-buffer))) + (t + (save-buffer) + (if clients + ;; Don't use `server-edit' because we do not want to + ;; show another buffer belonging to another client. + ;; See https://github.com/magit/magit/issues/2197. + (server-done) + (kill-buffer)))) + (when pid + (let ((default-directory dir)) + (process-file "kill" nil nil nil + "-s" (if cancel "USR2" "USR1") pid))) + (when (and winconf (eq (window-configuration-frame winconf) + (selected-frame))) + (set-window-configuration winconf)))) + +;;; Mode + +(defvar-keymap with-editor-mode-map + "C-c C-c" #'with-editor-finish + "<remap> <server-edit>" #'with-editor-finish + "<remap> <evil-save-and-close>" #'with-editor-finish + "<remap> <evil-save-modified-and-close>" #'with-editor-finish + "C-c C-k" #'with-editor-cancel + "<remap> <kill-buffer>" #'with-editor-cancel + "<remap> <ido-kill-buffer>" #'with-editor-cancel + "<remap> <iswitchb-kill-buffer>" #'with-editor-cancel + "<remap> <evil-quit>" #'with-editor-cancel) + +(define-minor-mode with-editor-mode + "Edit a file as the $EDITOR of an external process." + :lighter with-editor-mode-lighter + ;; Protect the user from killing the buffer without using + ;; either `with-editor-finish' or `with-editor-cancel', + ;; and from removing the key bindings for these commands. + (unless with-editor-mode + (user-error "With-Editor mode cannot be turned off")) + (add-hook 'kill-buffer-query-functions + #'with-editor-kill-buffer-noop nil t) + ;; `server-execute' displays a message which is not + ;; correct when using this mode. + (when with-editor-show-usage + (with-editor-usage-message))) + +(put 'with-editor-mode 'permanent-local t) + +(defun with-editor-kill-buffer-noop () + ;; We started doing this in response to #64, but it is not safe + ;; to do so, because the client has already been killed, causing + ;; `with-editor-return' (called by `with-editor-cancel') to delete + ;; the file, see #66. The reason we delete the file in the first + ;; place are https://github.com/magit/magit/issues/2258 and + ;; https://github.com/magit/magit/issues/2248. + ;; (if (memq this-command '(save-buffers-kill-terminal + ;; save-buffers-kill-emacs)) + ;; (let ((with-editor-cancel-query-functions nil)) + ;; (with-editor-cancel nil) + ;; t) + ;; ...) + ;; So go back to always doing this instead: + (user-error (substitute-command-keys (format "\ +Don't kill this buffer %S. Instead cancel using \\[with-editor-cancel]" + (current-buffer))))) + +(defvar-local with-editor-usage-message "\ +Type \\[with-editor-finish] to finish, \ +or \\[with-editor-cancel] to cancel") + +(defun with-editor-usage-message () + ;; Run after `server-execute', which is run using + ;; a timer which starts immediately. + (let ((buffer (current-buffer))) + (run-with-timer + 0.05 nil + (lambda () + (with-current-buffer buffer + (message (substitute-command-keys with-editor-usage-message))))))) + +;;; Wrappers + +(defvar with-editor--envvar nil "For internal use.") + +(defmacro with-editor (&rest body) + "Use the Emacsclient as $EDITOR while evaluating BODY. +Modify the `process-environment' for processes started in BODY, +instructing them to use the Emacsclient as $EDITOR. If optional +ENVVAR is a literal string then bind that environment variable +instead. +\n(fn [ENVVAR] BODY...)" + (declare (indent defun) (debug (body))) + `(let ((with-editor--envvar ,(if (stringp (car body)) + (pop body) + '(or with-editor--envvar "EDITOR"))) + (process-environment process-environment)) + (with-editor--setup) + ,@body)) + +(defmacro with-editor* (envvar &rest body) + "Use the Emacsclient as the editor while evaluating BODY. +Modify the `process-environment' for processes started in BODY, +instructing them to use the Emacsclient as editor. ENVVAR is the +environment variable that is exported to do so, it is evaluated +at run-time. +\n(fn [ENVVAR] BODY...)" + (declare (indent defun) (debug (sexp body))) + `(let ((with-editor--envvar ,envvar) + (process-environment process-environment)) + (with-editor--setup) + ,@body)) + +(defun with-editor--setup () + (if (or (not with-editor-emacsclient-executable) + (file-remote-p default-directory)) + (push (concat with-editor--envvar "=" with-editor-sleeping-editor) + process-environment) + ;; Make sure server-use-tcp's value is valid. + (unless (featurep 'make-network-process '(:family local)) + (setq server-use-tcp t)) + ;; Make sure the server is running. + (unless (process-live-p server-process) + (when (server-running-p server-name) + (setq server-name (format "server%s" (emacs-pid))) + (when (server-running-p server-name) + (server-force-delete server-name))) + (server-start)) + ;; Tell $EDITOR to use the Emacsclient. + (push (concat with-editor--envvar "=" + ;; Quoting is the right thing to do. Applications that + ;; fail because of that, are the ones that need fixing, + ;; e.g., by using 'eval "$EDITOR" file'. See #121. + (shell-quote-argument + ;; If users set the executable manually, they might + ;; begin the path with "~", which would get quoted. + (if (string-prefix-p "~" with-editor-emacsclient-executable) + (concat (expand-file-name "~") + (substring with-editor-emacsclient-executable 1)) + with-editor-emacsclient-executable)) + ;; Tell the process where the server file is. + (and (not server-use-tcp) + (concat " --socket-name=" + (shell-quote-argument + (expand-file-name server-name + server-socket-dir))))) + process-environment) + (when server-use-tcp + (push (concat "EMACS_SERVER_FILE=" + (expand-file-name server-name server-auth-dir)) + process-environment)) + ;; As last resort fallback to the sleeping editor. + (push (concat "ALTERNATE_EDITOR=" with-editor-sleeping-editor) + process-environment))) + +(defun with-editor-server-window () + (or (and buffer-file-name + (cdr (cl-find-if (lambda (cons) + (string-match-p (car cons) buffer-file-name)) + with-editor-server-window-alist))) + server-window)) + +(define-advice server-switch-buffer + (:around (fn &optional next-buffer &rest args) + with-editor-server-window-alist) + "Honor `with-editor-server-window-alist' (which see)." + (let ((server-window (with-current-buffer + (or next-buffer (current-buffer)) + (when with-editor-mode + (setq with-editor-previous-winconf + (current-window-configuration))) + (with-editor-server-window)))) + (apply fn next-buffer args))) + +(define-advice start-file-process + (:around (fn name buffer program &rest program-args) + with-editor-process-filter) + "When called inside a `with-editor' form and the Emacsclient +cannot be used, then give the process the filter function +`with-editor-process-filter'. To avoid overriding the filter +being added here you should use `with-editor-set-process-filter' +instead of `set-process-filter' inside `with-editor' forms. + +When the `default-directory' is located on a remote machine, +then also manipulate PROGRAM and PROGRAM-ARGS in order to set +the appropriate editor environment variable." + (if (not with-editor--envvar) + (apply fn name buffer program program-args) + (when (file-remote-p default-directory) + (unless (equal program "env") + (push program program-args) + (setq program "env")) + (push (concat with-editor--envvar "=" with-editor-sleeping-editor) + program-args)) + (let ((process (apply fn name buffer program program-args))) + (set-process-filter process #'with-editor-process-filter) + (process-put process 'default-dir default-directory) + process))) + +(advice-add #'make-process :around + #'make-process@with-editor-process-filter) +(cl-defun make-process@with-editor-process-filter + (fn &rest keys &key name buffer command coding noquery stop + connection-type filter sentinel stderr file-handler + &allow-other-keys) + "When called inside a `with-editor' form and the Emacsclient +cannot be used, then give the process the filter function +`with-editor-process-filter'. To avoid overriding the filter +being added here you should use `with-editor-set-process-filter' +instead of `set-process-filter' inside `with-editor' forms. + +When the `default-directory' is located on a remote machine and +FILE-HANDLER is non-nil, then also manipulate COMMAND in order +to set the appropriate editor environment variable." + (if (or (not file-handler) (not with-editor--envvar)) + (apply fn keys) + (when (file-remote-p default-directory) + (unless (equal (car command) "env") + (push "env" command)) + (push (concat with-editor--envvar "=" with-editor-sleeping-editor) + (cdr command))) + (let* ((filter (if filter + (lambda (process output) + (funcall filter process output) + (with-editor-process-filter process output t)) + #'with-editor-process-filter)) + (process (funcall fn + :name name + :buffer buffer + :command command + :coding coding + :noquery noquery + :stop stop + :connection-type connection-type + :filter filter + :sentinel sentinel + :stderr stderr + :file-handler file-handler))) + (process-put process 'default-dir default-directory) + process))) + +(defun with-editor-set-process-filter (process filter) + "Like `set-process-filter' but keep `with-editor-process-filter'. +Give PROCESS the new FILTER but keep `with-editor-process-filter' +if that was added earlier by the advised `start-file-process'. + +Do so by wrapping the two filter functions using a lambda, which +becomes the actual filter. It calls FILTER first, which may or +may not insert the text into the PROCESS's buffer. Then it calls +`with-editor-process-filter', passing t as NO-STANDARD-FILTER." + (set-process-filter + process + (if (eq (process-filter process) 'with-editor-process-filter) + `(lambda (proc str) + (,filter proc str) + (with-editor-process-filter proc str t)) + filter))) + +(defvar with-editor-filter-visit-hook nil) + +(defconst with-editor-sleeping-editor-regexp "^\ +WITH-EDITOR: \\([0-9]+\\) \ +OPEN \\([^]+?\\)\ +\\(?:\\([^]*\\)\\)?\ +\\(?: IN \\([^\r]+?\\)\\)?\r?$") + +(defvar with-editor--max-incomplete-length 1000) + +(defun with-editor-sleeping-editor-filter (process string) + (when-let ((incomplete (and process (process-get process 'incomplete)))) + (setq string (concat incomplete string))) + (save-match-data + (cond + ((and process (not (string-suffix-p "\n" string))) + (let ((length (length string))) + (when (> length with-editor--max-incomplete-length) + (setq string + (substring string + (- length with-editor--max-incomplete-length))))) + (process-put process 'incomplete string) + nil) + ((string-match with-editor-sleeping-editor-regexp string) + (when process + (process-put process 'incomplete nil)) + (let ((pid (match-string 1 string)) + (arg0 (match-string 2 string)) + (arg1 (match-string 3 string)) + (dir (match-string 4 string)) + file line column) + (cond ((string-match "\\`\\+\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?\\'" arg0) + (setq file arg1) + (setq line (string-to-number (match-string 1 arg0))) + (setq column (match-string 2 arg0)) + (setq column (and column (string-to-number column)))) + ((setq file arg0))) + (unless (file-name-absolute-p file) + (setq file (expand-file-name file dir))) + (when default-directory + (setq file (concat (file-remote-p default-directory) file))) + (with-current-buffer (find-file-noselect file) + (with-editor-mode 1) + (setq with-editor--pid pid) + (setq with-editor-previous-winconf + (current-window-configuration)) + (when line + (let ((pos (save-excursion + (save-restriction + (goto-char (point-min)) + (forward-line (1- line)) + (when column + (move-to-column column)) + (point))))) + (when (and (buffer-narrowed-p) + widen-automatically + (not (<= (point-min) pos (point-max)))) + (widen)) + (goto-char pos))) + (run-hooks 'with-editor-filter-visit-hook) + (funcall (or (with-editor-server-window) #'switch-to-buffer) + (current-buffer)) + (kill-local-variable 'server-window))) + nil) + (t string)))) + +(defun with-editor-process-filter + (process string &optional no-default-filter) + "Listen for edit requests by child processes." + (let ((default-directory (process-get process 'default-dir))) + (with-editor-sleeping-editor-filter process string)) + (unless no-default-filter + (internal-default-process-filter process string))) + +(define-advice server-visit-files + (:after (files _proc &optional _nowait) + with-editor-file-name-history-exclude) + "Prevent certain files from being added to `file-name-history'. +Files matching a regexp in `with-editor-file-name-history-exclude' +are prevented from being added to that list." + (pcase-dolist (`(,file . ,_) files) + (when (cl-find-if (lambda (regexp) + (string-match-p regexp file)) + with-editor-file-name-history-exclude) + (setq file-name-history (delete file file-name-history))))) + +;;; Augmentations + +;;;###autoload +(cl-defun with-editor-export-editor (&optional (envvar "EDITOR")) + "Teach subsequent commands to use current Emacs instance as editor. + +Set and export the environment variable ENVVAR, by default +\"EDITOR\". The value is automatically generated to teach +commands to use the current Emacs instance as \"the editor\". + +This works in `shell-mode', `term-mode', `eshell-mode' and +`vterm'." + (interactive (list (with-editor-read-envvar))) + (cond + ((derived-mode-p 'comint-mode 'term-mode) + (when-let ((process (get-buffer-process (current-buffer)))) + (goto-char (process-mark process)) + (process-send-string + process (format " export %s=%s\n" envvar + (shell-quote-argument with-editor-sleeping-editor))) + (while (accept-process-output process 0.1)) + (if (derived-mode-p 'term-mode) + (with-editor-set-process-filter process #'with-editor-emulate-terminal) + (add-hook 'comint-output-filter-functions #'with-editor-output-filter + nil t)))) + ((derived-mode-p 'eshell-mode) + (add-to-list 'eshell-preoutput-filter-functions + #'with-editor-output-filter) + (setenv envvar with-editor-sleeping-editor)) + ((and (derived-mode-p 'vterm-mode) + (fboundp 'vterm-send-return) + (fboundp 'vterm-send-string)) + (if with-editor-emacsclient-executable + (let ((with-editor--envvar envvar) + (process-environment process-environment)) + (with-editor--setup) + (while (accept-process-output vterm--process 0.1)) + (when-let ((v (getenv envvar))) + (vterm-send-string (format " export %s=%S" envvar v)) + (vterm-send-return)) + (when-let ((v (getenv "EMACS_SERVER_FILE"))) + (vterm-send-string (format " export EMACS_SERVER_FILE=%S" v)) + (vterm-send-return)) + (vterm-send-string "clear") + (vterm-send-return)) + (error "Cannot use sleeping editor in this buffer"))) + (t + (error "Cannot export environment variables in this buffer"))) + (message "Successfully exported %s" envvar)) + +;;;###autoload +(defun with-editor-export-git-editor () + "Like `with-editor-export-editor' but always set `$GIT_EDITOR'." + (interactive) + (with-editor-export-editor "GIT_EDITOR")) + +;;;###autoload +(defun with-editor-export-hg-editor () + "Like `with-editor-export-editor' but always set `$HG_EDITOR'." + (interactive) + (with-editor-export-editor "HG_EDITOR")) + +(defun with-editor-output-filter (string) + "Handle edit requests on behalf of `comint-mode' and `eshell-mode'." + (with-editor-sleeping-editor-filter nil string)) + +(defun with-editor-emulate-terminal (process string) + "Like `term-emulate-terminal' but also handle edit requests." + (let ((with-editor-sleeping-editor-regexp + (substring with-editor-sleeping-editor-regexp 1))) + (with-editor-sleeping-editor-filter process string)) + (term-emulate-terminal process string)) + +(defvar with-editor-envvars '("EDITOR" "GIT_EDITOR" "HG_EDITOR")) + +(cl-defun with-editor-read-envvar + (&optional (prompt "Set environment variable") + (default "EDITOR")) + (let ((reply (completing-read (if default + (format "%s (%s): " prompt default) + (concat prompt ": ")) + with-editor-envvars nil nil nil nil default))) + (if (string= reply "") (user-error "Nothing selected") reply))) + +;;;###autoload +(define-minor-mode shell-command-with-editor-mode + "Teach `shell-command' to use current Emacs instance as editor. + +Teach `shell-command', and all commands that ultimately call that +command, to use the current Emacs instance as editor by executing +\"EDITOR=CLIENT COMMAND&\" instead of just \"COMMAND&\". + +CLIENT is automatically generated; EDITOR=CLIENT instructs +COMMAND to use to the current Emacs instance as \"the editor\", +assuming no other variable overrides the effect of \"$EDITOR\". +CLIENT may be the path to an appropriate emacsclient executable +with arguments, or a script which also works over Tramp. + +Alternatively you can use the `with-editor-async-shell-command', +which also allows the use of another variable instead of +\"EDITOR\"." + :global t) + +;;;###autoload +(defun with-editor-async-shell-command + (command &optional output-buffer error-buffer envvar) + "Like `async-shell-command' but with `$EDITOR' set. + +Execute string \"ENVVAR=CLIENT COMMAND\" in an inferior shell; +display output, if any. With a prefix argument prompt for an +environment variable, otherwise the default \"EDITOR\" variable +is used. With a negative prefix argument additionally insert +the COMMAND's output at point. + +CLIENT is automatically generated; ENVVAR=CLIENT instructs +COMMAND to use to the current Emacs instance as \"the editor\", +assuming it respects ENVVAR as an \"EDITOR\"-like variable. +CLIENT may be the path to an appropriate emacsclient executable +with arguments, or a script which also works over Tramp. + +Also see `async-shell-command' and `shell-command'." + (interactive (with-editor-shell-command-read-args "Async shell command: " t)) + (let ((with-editor--envvar envvar)) + (with-editor + (async-shell-command command output-buffer error-buffer)))) + +;;;###autoload +(defun with-editor-shell-command + (command &optional output-buffer error-buffer envvar) + "Like `shell-command' or `with-editor-async-shell-command'. +If COMMAND ends with \"&\" behave like the latter, +else like the former." + (interactive (with-editor-shell-command-read-args "Shell command: ")) + (if (string-match "&[ \t]*\\'" command) + (with-editor-async-shell-command + command output-buffer error-buffer envvar) + (shell-command command output-buffer error-buffer))) + +(defun with-editor-shell-command-read-args (prompt &optional async) + (let ((command (read-shell-command + prompt nil nil + (let ((filename (or buffer-file-name + (and (eq major-mode 'dired-mode) + (dired-get-filename nil t))))) + (and filename (file-relative-name filename)))))) + (list command + (if (or async (setq async (string-match-p "&[ \t]*\\'" command))) + (< (prefix-numeric-value current-prefix-arg) 0) + current-prefix-arg) + shell-command-default-error-buffer + (and async current-prefix-arg (with-editor-read-envvar))))) + +(define-advice shell-command + (:around (fn command &optional output-buffer error-buffer) + shell-command-with-editor-mode) + "`shell-mode' and its hook are intended for buffers in which an +interactive shell is running, but `shell-command' also turns on +that mode, even though it only runs the shell to run a single +command. The `with-editor-export-editor' hook function is only +intended to be used in buffers in which an interactive shell is +running, so it has to be removed here." + (let ((shell-mode-hook (remove 'with-editor-export-editor shell-mode-hook))) + (cond ((or (not (or with-editor--envvar shell-command-with-editor-mode)) + (not (string-suffix-p "&" command))) + (funcall fn command output-buffer error-buffer)) + ((and with-editor-shell-command-use-emacsclient + with-editor-emacsclient-executable + (not (file-remote-p default-directory))) + (with-editor (funcall fn command output-buffer error-buffer))) + (t + (funcall fn (format "%s=%s %s" + (or with-editor--envvar "EDITOR") + (shell-quote-argument with-editor-sleeping-editor) + command) + output-buffer error-buffer) + (ignore-errors + (let ((process (get-buffer-process + (or output-buffer + (get-buffer "*Async Shell Command*"))))) + (set-process-filter + process (lambda (proc str) + (comint-output-filter proc str) + (with-editor-process-filter proc str t))) + process)))))) + +;;; _ + +(defun with-editor-debug () + "Debug configuration issues. +See info node `(with-editor)Debugging' for instructions." + (interactive) + (require 'warnings) + (with-current-buffer (get-buffer-create "*with-editor-debug*") + (pop-to-buffer (current-buffer)) + (erase-buffer) + (ignore-errors (with-editor)) + (insert + (format "with-editor: %s\n" (locate-library "with-editor.el")) + (format "emacs: %s (%s)\n" + (expand-file-name invocation-name invocation-directory) + emacs-version) + "system:\n" + (format " system-type: %s\n" system-type) + (format " system-configuration: %s\n" system-configuration) + (format " system-configuration-options: %s\n" system-configuration-options) + "server:\n" + (format " server-running-p: %s\n" (server-running-p)) + (format " server-process: %S\n" server-process) + (format " server-use-tcp: %s\n" server-use-tcp) + (format " server-name: %s\n" server-name) + (format " server-socket-dir: %s\n" server-socket-dir)) + (if (and server-socket-dir (file-accessible-directory-p server-socket-dir)) + (dolist (file (directory-files server-socket-dir nil "^[^.]")) + (insert (format " %s\n" file))) + (insert (format " %s: not an accessible directory\n" + (if server-use-tcp "WARNING" "ERROR")))) + (insert (format " server-auth-dir: %s\n" server-auth-dir)) + (if (file-accessible-directory-p server-auth-dir) + (dolist (file (directory-files server-auth-dir nil "^[^.]")) + (insert (format " %s\n" file))) + (insert (format " %s: not an accessible directory\n" + (if server-use-tcp "ERROR" "WARNING")))) + (let ((val with-editor-emacsclient-executable) + (def (default-value 'with-editor-emacsclient-executable)) + (fun (let ((warning-minimum-level :error) + (warning-minimum-log-level :error)) + (with-editor-locate-emacsclient)))) + (insert "with-editor-emacsclient-executable:\n" + (format " value: %s (%s)\n" val + (and val (with-editor-emacsclient-version val))) + (format " default: %s (%s)\n" def + (and def (with-editor-emacsclient-version def))) + (format " funcall: %s (%s)\n" fun + (and fun (with-editor-emacsclient-version fun))))) + (insert "path:\n" + (format " $PATH: %s\n" (split-string (getenv "PATH") ":")) + (format " exec-path: %s\n" exec-path)) + (insert (format " with-editor-emacsclient-path:\n")) + (dolist (dir (with-editor-emacsclient-path)) + (insert (format " %s (%s)\n" dir (car (file-attributes dir)))) + (when (file-directory-p dir) + ;; Don't match emacsclientw.exe, it makes popup windows. + (dolist (exec (directory-files dir t "emacsclient\\(?:[^w]\\|\\'\\)")) + (insert (format " %s (%s)\n" exec + (with-editor-emacsclient-version exec)))))))) + +(defconst with-editor-font-lock-keywords + '(("(\\(with-\\(?:git-\\)?editor\\)\\_>" (1 'font-lock-keyword-face)))) +(font-lock-add-keywords 'emacs-lisp-mode with-editor-font-lock-keywords) + +(provide 'with-editor) +;; Local Variables: +;; indent-tabs-mode: nil +;; byte-compile-warnings: (not docstrings-control-chars) +;; End: +;;; with-editor.el ends here diff --git a/emacs/elpa/with-editor-20240609.1518/with-editor.elc b/emacs/elpa/with-editor-20240609.1518/with-editor.elc Binary files differ. diff --git a/emacs/elpa/with-editor-20240415.1558/with-editor.info b/emacs/elpa/with-editor-20240609.1518/with-editor.info