config

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

commit 34fa8684b51954376abb8f69f02c387b879ecdb8
parent 4b3d2da8152ceace3b16771d80ae896dd2838481
Author: dwrz <dwrz@dwrz.net>
Date:   Thu,  4 Apr 2024 01:21:19 +0000

Update Emacs packages

Diffstat:
Memacs/elpa/archives/gnu/archive-contents | 22+++++++++++-----------
Memacs/elpa/archives/gnu/archive-contents.signed | 4++--
Memacs/elpa/archives/melpa/archive-contents | 135++++++++++++++++++++++++++++++++++++++-----------------------------------------
Memacs/elpa/archives/nongnu/archive-contents.signed | 6+++---
Demacs/elpa/ledger-mode-20240326.1834/dir | 18------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-check.el | 146-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-check.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-commodities.el | 160-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-commodities.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-complete.el | 387-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-complete.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-context.el | 215-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-context.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-exec.el | 126-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-exec.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-flymake.el | 146-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-flymake.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-fontify.el | 58----------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-fontify.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-fonts.el | 684-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-fonts.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-init.el | 97-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-init.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-mode-autoloads.el | 140-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-mode-pkg.el | 6------
Demacs/elpa/ledger-mode-20240326.1834/ledger-mode.el | 339-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-mode.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-mode.info | 1554-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-navigate.el | 199-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-navigate.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-occur.el | 178-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-occur.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-post.el | 195-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-post.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-reconcile.el | 700-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-reconcile.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-regex.el | 428-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-regex.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-report.el | 668-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-report.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-schedule.el | 332-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-schedule.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-sort.el | 119-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-sort.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-state.el | 259-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-state.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-test.el | 137-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-test.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-texi.el | 178-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-texi.elc | 0
Demacs/elpa/ledger-mode-20240326.1834/ledger-xact.el | 220-------------------------------------------------------------------------------
Demacs/elpa/ledger-mode-20240326.1834/ledger-xact.elc | 0
Demacs/elpa/marginalia-20240323.2015/marginalia-autoloads.el | 56--------------------------------------------------------
Demacs/elpa/marginalia-20240323.2015/marginalia-pkg.el | 15---------------
Demacs/elpa/marginalia-20240323.2015/marginalia.el | 1361-------------------------------------------------------------------------------
Demacs/elpa/marginalia-20240323.2015/marginalia.elc | 0
Demacs/elpa/notmuch-20231006.2337/coolj.el | 145-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/coolj.elc | 0
Demacs/elpa/notmuch-20231006.2337/make-deps.el | 69---------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/make-deps.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-address.el | 436-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-address.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-autoloads.el | 215-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-company.el | 106-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-company.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-compat.el | 58----------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-compat.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-crypto.el | 272-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-crypto.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-draft.el | 287-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-draft.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-hello.el | 1015-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-hello.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-jump.el | 210-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-jump.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-lib.el | 1075-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-lib.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-logo.svg | 27---------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-maildir-fcc.el | 362-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-maildir-fcc.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-message.el | 76----------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-message.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-mua.el | 651-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-mua.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-parser.el | 194-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-parser.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-pkg.el | 4----
Demacs/elpa/notmuch-20231006.2337/notmuch-print.el | 100-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-print.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-query.el | 74--------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-query.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-show.el | 2737-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-show.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-tag.el | 587-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-tag.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-tree.el | 1467-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-tree.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch-wash.el | 418-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch-wash.elc | 0
Demacs/elpa/notmuch-20231006.2337/notmuch.el | 1239-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/notmuch.elc | 0
Demacs/elpa/notmuch-20231006.2337/rstdoc.el | 90-------------------------------------------------------------------------------
Demacs/elpa/notmuch-20231006.2337/rstdoc.elc | 0
103 files changed, 81 insertions(+), 21121 deletions(-)

diff --git a/emacs/elpa/archives/gnu/archive-contents b/emacs/elpa/archives/gnu/archive-contents @@ -1300,7 +1300,7 @@ (:url . "https://elpa.gnu.org/packages/elisp-benchmarks.html") (:commit . "70e38dbfa8f4acbdebfd0f417410d99f5031e05f"))]) (ellama . - [(0 8 13) + [(0 9 0) ((emacs (28 1)) (llm @@ -1313,7 +1313,7 @@ (:maintainer "Sergey Kostyaev" . "sskostyaev@gmail.com") (:authors ("Sergey Kostyaev" . "sskostyaev@gmail.com")) - (:commit . "b94d5952d4e36e1caeef5ac0b3ecd6d89009082b"))]) + (:commit . "a931224a0d096938f912da7a125fbd569cec6251"))]) (emacs-gc-stats . [(1 4 2) ((emacs @@ -2151,7 +2151,7 @@ (:url . "https://elpa.gnu.org/packages/jgraph-mode.html") (:commit . "4e13f89fe8837b84d40b969e6a5431816180a747"))]) (jinx . - [(1 4) + [(1 5) ((emacs (27 1)) (compat @@ -2162,7 +2162,7 @@ (:maintainer "Daniel Mendler" . "mail@daniel-mendler.de") (:authors ("Daniel Mendler" . "mail@daniel-mendler.de")) - (:commit . "b2ef3af2857896e20cff0488c46a4bfcfee39ecc"))]) + (:commit . "a3d69abcb44555ddf5dbf03eee3c6188a456eceb"))]) (jit-spell . [(0 4) ((emacs @@ -2470,7 +2470,7 @@ (:url . "https://elpa.gnu.org/packages/map.html") (:commit . "9da2efb670574b473ab864ae0456b4f1b38e680b"))]) (marginalia . - [(1 5) + [(1 6) ((emacs (27 1)) (compat @@ -2484,7 +2484,7 @@ (:authors ("Omar Antolín Camarena" . "omar@matem.unam.mx") ("Daniel Mendler" . "mail@daniel-mendler.de")) - (:commit . "98f6e58c12d57283bd7c1cb241664c966dc38ac3"))]) + (:commit . "58eb5fd6e5cc21b12c5455ae69e7ae93579647bc"))]) (markchars . [(0 2 2) nil "Mark chars fitting certain characteristics" tar @@ -2748,13 +2748,13 @@ (:url . "https://elpa.gnu.org/packages/nlinum.html") (:commit . "99d0fef381e9f44a3fdcf66f28c28109a7cdaf45"))]) (notes-mode . - [(1 30) + [(1 31) nil "Indexing system for on-line note-taking" tar - ((:maintainer nil . "johnh@isi.edu") + ((:url . "https://ant.isi.edu/~johnh/SOFTWARE/NOTES_MODE/") + (:maintainer "John Heidemann" . "johnh@isi.edu") (:authors - (nil . "johnh@isi.edu")) - (:url . "https://elpa.gnu.org/packages/notes-mode.html") - (:commit . "3afb72a580149460190cb9dd8e18b274a4e24e1e"))]) + ("John Heidemann" . "johnh@isi.edu")) + (:commit . "2a25d79f7e5d9ab7298ba40e11e78d1f2ded06d2"))]) (notmuch-indicator . [(1 1 0) ((emacs diff --git a/emacs/elpa/archives/gnu/archive-contents.signed b/emacs/elpa/archives/gnu/archive-contents.signed @@ -1 +1 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasign@elpa.gnu.org> (trust undefined) created at 2024-04-01T09:10:03+0000 using RSA -\ No newline at end of file +Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasign@elpa.gnu.org> (trust undefined) created at 2024-04-04T09:10:03+0000 using RSA +\ No newline at end of file diff --git a/emacs/elpa/archives/melpa/archive-contents b/emacs/elpa/archives/melpa/archive-contents @@ -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 . [(20240331 2055) ((emacs (24 3))) "A major mode for editing Breccian text" tar ((:commit . "e3e914a7f86a5e39e400767b4ea8c0c5016bc123") (: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 . [(20240401 2103) ((emacs (24 3))) "A major mode for editing Breccian text" tar ((:commit . "7a056075e87556df6a3742c390b2e28e071773df") (: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"))]) @@ -487,7 +487,7 @@ (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 . [(20240330 449) ((emacs (29 1))) "Transient UI for Calc" tar ((:commit . "ace04facf96e71b097d3ba83f64b1963af4b8f13") (: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 . [(20240402 418) ((emacs (29 1))) "Transient UI for Calc" tar ((:commit . "c5831803c5559370c66cc34ff39734fc896b9f83") (: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"))]) (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"))]) (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"))]) @@ -526,8 +526,8 @@ (chapel-mode . [(20210513 457) ((emacs (25 1)) (hydra (0 15 0))) "A major mode for the Chapel programming language" tar ((:commit . "39fd24bb7cf44808200354ac0496be4fc4fddd9a") (:keywords "chapel" "chpl" "programming" "languages") (:url . "https://github.com/damon-kwok/chapel-mode"))]) (char-menu . [(20210321 1657) ((emacs (24 3)) (avy-menu (0 1))) "Create your own menu for fast insertion of arbitrary symbols" tar ((:commit . "d77c4d64fc8acc386a0fb9727d346c838e75f011") (:authors ("Mark Karpov" . "markkarpov92@gmail.com")) (:maintainers ("Mark Karpov" . "markkarpov92@gmail.com")) (:maintainer "Mark Karpov" . "markkarpov92@gmail.com") (:keywords "convenience" "editing") (:url . "https://github.com/mrkkrp/char-menu"))]) (charmap . [(20200616 1418) nil "Unicode table for Emacs" tar ((:commit . "feac50b87d2a596c5e5b7b82b79ddd65b6dedd8c") (:authors ("Anan Mikami" . "lateau@gmail.com")) (:maintainers ("Anan Mikami" . "lateau@gmail.com")) (:maintainer "Anan Mikami" . "lateau@gmail.com") (:keywords "unicode" "character" "ucs") (:url . "https://github.com/lateau/charmap"))]) - (chatgpt-shell . [(20240306 857) ((emacs (27 1)) (shell-maker (0 50 1))) "ChatGPT shell + buffer insert commands" tar ((:commit . "bf2d12ed2ed60c498d95215fa1cf81c2b23191a7") (:authors ("Alvaro Ramirez https://xenodium.com")) (:maintainers ("Alvaro Ramirez https://xenodium.com")) (:maintainer "Alvaro Ramirez https://xenodium.com") (:url . "https://github.com/xenodium/chatgpt-shell"))]) - (chatu . [(20240308 1129) ((org (9 6 6)) (emacs (29 1)) (plantuml-mode (1 2 9))) "Convert and insert any images to org-mode or markdown buffer" tar ((:commit . "63a42219b28b2011e86ae5e8abb1366b65a25347") (:authors ("Kimi Ma" . "kimi.im@outlook.com")) (:maintainers ("Kimi Ma" . "kimi.im@outlook.com")) (:maintainer "Kimi Ma" . "kimi.im@outlook.com") (:keywords "multimedia" "convenience") (:url . "https://github.com/kimim/chatu"))]) + (chatgpt-shell . [(20240402 2216) ((emacs (27 1)) (shell-maker (0 50 1))) "ChatGPT shell + buffer insert commands" tar ((:commit . "42cf53ba7d43e0622e8c4c57ef8f635e1248182b") (:authors ("Alvaro Ramirez https://xenodium.com")) (:maintainers ("Alvaro Ramirez https://xenodium.com")) (:maintainer "Alvaro Ramirez https://xenodium.com") (:url . "https://github.com/xenodium/chatgpt-shell"))]) + (chatu . [(20240403 1339) ((org (9 6 6)) (emacs (29 1)) (plantuml-mode (1 2 9))) "Convert and insert any images to org-mode or markdown buffer" tar ((:commit . "0bc98ca7f9665a04c5c9d8c3152a98bf04933f0c") (:authors ("Kimi Ma" . "kimi.im@outlook.com")) (:maintainers ("Kimi Ma" . "kimi.im@outlook.com")) (:maintainer "Kimi Ma" . "kimi.im@outlook.com") (:keywords "multimedia" "convenience") (:url . "https://github.com/kimim/chatu"))]) (chatwork . [(20170511 442) nil "ChatWork client for Emacs" tar ((:commit . "fea231d479f06bf40dbfcf45de143eecc9ed744c") (:authors ("Masayuki Ataka" . "masayuki.ataka@gmail.com")) (:maintainers ("Masayuki Ataka" . "masayuki.ataka@gmail.com")) (:maintainer "Masayuki Ataka" . "masayuki.ataka@gmail.com") (:keywords "web") (:url . "https://github.com/ataka/chatwork"))]) (cheat-sh . [(20210607 1307) ((emacs (25 1))) "Interact with cheat.sh" tar ((:commit . "33bae22feae8d3375739c6bdef08d0dcdf47ee42") (:authors ("Dave Pearson" . "davep@davep.org")) (:maintainers ("Dave Pearson" . "davep@davep.org")) (:maintainer "Dave Pearson" . "davep@davep.org") (:keywords "docs" "help") (:url . "https://github.com/davep/cheat-sh.el"))]) (cheatsheet . [(20170126 2150) ((emacs (24)) (cl-lib (0 5))) "create your own cheatsheet" tar ((:commit . "e4f8e0110167ea16a17a74517d1f10cb7ff805b8") (:authors ("Shirin Nikita" . "shirin.nikita@gmail.com")) (:maintainers ("Shirin Nikita" . "shirin.nikita@gmail.com")) (:maintainer "Shirin Nikita" . "shirin.nikita@gmail.com") (:keywords "convenience" "usability") (:url . "http://github.com/darksmile/cheatsheet/"))]) @@ -581,7 +581,7 @@ (clang-capf . [(20221030 1830) ((emacs (24 4))) "Completion-at-point backend for c/c++ using clang" tar ((:commit . "5e4dfba90ce86bbc7ee61805edfca04fff93c291") (:authors ("Philip K. <philipk [at] posteo [dot] net>")) (:maintainers ("Philip K. <philipk [at] posteo [dot] net>")) (:maintainer "Philip K. <philipk [at] posteo [dot] net>") (:keywords "c" "abbrev" "convenience") (:url . "https://git.sr.ht/~pkal/clang-capf"))]) (clang-format . [(20240115 1750) ((cl-lib (0 3))) "Format code using clang-format" tar ((:commit . "9f4358fcc8b04018cc1ed46fcc96fc7bfa361a47") (:keywords "tools" "c"))]) (clang-format+ . [(20190824 2216) ((emacs (25 1)) (clang-format (20180406 1514))) "Minor mode for automatic clang-format application" tar ((:commit . "ddd4bfe1a13c2fd494ce339a320a51124c1d2f68") (:keywords "c" "c++" "clang-format") (:url . "https://github.com/SavchenkoValeriy/emacs-clang-format-plus"))]) - (claude-shell . [(20240326 1721) ((emacs (29 1)) (shell-maker (0 50 1))) "Integration with Anthropic's Claude LLM" tar ((:commit . "0ade87053de313ea63d05bce7c5ce344e976a8ff") (:authors ("Armin Friedl" . "dev@friedl.net")) (:maintainers ("Armin Friedl" . "dev@friedl.net")) (:maintainer "Armin Friedl" . "dev@friedl.net") (:keywords "anthropic" "claude" "claude-shell" "shell-maker" "terminals" "wp" "help" "tools") (:url . "https://github.com/arminfriedl/claude-shell"))]) + (claude-shell . [(20240401 1228) ((emacs (29 1)) (shell-maker (0 50 1))) "Integration with Anthropic's Claude LLM" tar ((:commit . "675c0cc7f2180773273c5d996759dff7a1c69f41") (:authors ("Armin Friedl" . "dev@friedl.net")) (:maintainers ("Armin Friedl" . "dev@friedl.net")) (:maintainer "Armin Friedl" . "dev@friedl.net") (:keywords "anthropic" "claude" "claude-shell" "shell-maker" "terminals" "wp" "help" "tools") (:url . "https://github.com/arminfriedl/claude-shell"))]) (clause . [(20230405 1235) ((emacs (27 1)) (mark-thing-at (0 3))) "Functions to move, mark, kill by clause" tar ((:commit . "0ea166fa218618c1b80b60c995f927310c25b02a") (:authors ("Marty Hiatt <martianhiatus [a t] riseup [dot] net>")) (:maintainers ("Marty Hiatt <martianhiatus [a t] riseup [dot] net>")) (:maintainer "Marty Hiatt <martianhiatus [a t] riseup [dot] net>") (:keywords "wp" "convenience" "sentences" "text") (:url . "https://codeberg.org/martianh/clause.el"))]) (clay . [(20240330 1314) ((emacs (26 1)) (cider (1 0))) "Emacs commands Clay - literate in Clojure" tar ((:commit . "b850e1024df3dec8e579a75b012a2c08d59c02ce") (:authors ("daslu")) (:maintainers ("daslu")) (:maintainer "daslu") (:keywords "lisp") (:url . "https://github.com/scicloj/clay.el"))]) (clean-aindent-mode . [(20171017 2043) nil "Simple indent and unindent, trims indent white-space" tar ((:commit . "a97bcae8f43a9ff64e95473e4ef0d8bafe829211") (:authors ("peter marinov" . "efravia@gmail.com")) (:maintainers ("peter marinov" . "efravia@gmail.com")) (:maintainer "peter marinov" . "efravia@gmail.com") (:keywords "indentation" "whitespace" "backspace") (:url . "https://github.com/pmarinov/clean-aindent-mode"))]) @@ -626,7 +626,7 @@ (cm-mode . [(20170203 2107) ((cl-lib (0 5))) "Minor mode for CriticMarkup" tar ((:commit . "276d49c859822265070ae5dfbb403fd7d8d06436") (:authors ("Joost Kremers" . "joostkremers@fastmail.fm")) (:maintainers ("Joost Kremers" . "joostkremers@fastmail.fm")) (:maintainer "Joost Kremers" . "joostkremers@fastmail.fm") (:keywords "text" "markdown"))]) (cmake-font-lock . [(20230304 2223) ((cmake-mode (0 0))) "Advanced, type aware, highlight support for CMake" tar ((:commit . "a6038e916bcca807ae695f7d7e5c300c3f38f415") (:authors ("Anders Lindgren")) (:maintainers ("Anders Lindgren")) (:maintainer "Anders Lindgren") (:keywords "faces" "languages") (:url . "https://github.com/Lindydancer/cmake-font-lock"))]) (cmake-ide . [(20210610 1525) ((emacs (24 4)) (cl-lib (0 5)) (seq (1 11)) (levenshtein (0)) (s (1 11 0))) "Calls CMake to find out include paths and other compiler flags" tar ((:commit . "28dc4ab5bd01d99553901b4efeb7234280928b18") (:authors ("Atila Neves" . "atila.neves@gmail.com")) (:maintainers ("Atila Neves" . "atila.neves@gmail.com")) (:maintainer "Atila Neves" . "atila.neves@gmail.com") (:keywords "languages") (:url . "http://github.com/atilaneves/cmake-ide"))]) - (cmake-mode . [(20240321 1332) ((emacs (24 1))) "major-mode for editing CMake sources" tar ((:commit . "25d2850c190ba915a7527a29dda6efe61be901f0"))]) + (cmake-mode . [(20240403 1545) ((emacs (24 1))) "major-mode for editing CMake sources" tar ((:commit . "a4774b0c10e9ce5354b9a7a721f8ff2952d1f247"))]) (cmake-project . [(20171121 1115) nil "Integrates CMake build process with Emacs" tar ((:commit . "d3f408f226eff3f77f7e00dd519f4efc78fd292d") (:authors ("Alexander Lamaison" . "alexander.lamaison@gmail")) (:maintainers ("Alexander Lamaison" . "alexander.lamaison@gmail")) (:maintainer "Alexander Lamaison" . "alexander.lamaison@gmail") (:keywords "c" "cmake" "languages" "tools") (:url . "http://github.com/alamaison/emacs-cmake-project"))]) (cmd-to-echo . [(20161203 2133) ((emacs (24 4)) (s (1 11 0)) (shell-split-string (20151224 208))) "Show the output of long-running commands in the echo area" tar ((:commit . "e0e874fc0e1ad6d291e39ed76023445297ad438a") (:authors ("Tijs Mallaerts" . "tijs.mallaerts@gmail.com")) (:maintainers ("Tijs Mallaerts" . "tijs.mallaerts@gmail.com")) (:maintainer "Tijs Mallaerts" . "tijs.mallaerts@gmail.com"))]) (cmm-mode . [(20150225 746) nil "Major mode for C-- source code" tar ((:commit . "c3ad514dff3eb30434f6b20d953276d4c00de1ee"))]) @@ -752,7 +752,7 @@ (compile-multi . [(20240315 2126) ((emacs (28 1))) "A multi target interface to compile" tar ((:commit . "7124939e77da2bf3847aeeba2ba0387e47e11ce7") (: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 . [(20231226 2123) ((emacs (28 0)) (all-the-icons-completion (0 0 1))) "Affixate `compile-multi' with icons" tar ((:commit . "3d5b72478fc5fe0c0fd7431daec516a8ccf3ec76") (: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 . [(20240401 1053) ((emacs (26 1)) (request (0 3 0))) "Compiler explorer client (godbolt.org)" tar ((:commit . "7c243f46c14ceaee64b79ec401f4f85434b54c47") (: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 . [(20240403 2046) ((emacs (26 1)) (request (0 3 0))) "Compiler explorer client (godbolt.org)" tar ((:commit . "5f4207ce292f46a6dcc0969bb549e33b464b456e") (: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"))]) @@ -857,7 +857,7 @@ (crm-custom . [(20160117 6) ((cl-lib (0 5))) "Alternate `completing-read-multiple' that uses `completing-read'" tar ((:commit . "f1aaccf64306a5f99d9bf7ba815d7ea41c15518d") (:authors ("Ryan C. Thompson" . "rct@thompsonclan.org")) (:maintainers ("Ryan C. Thompson" . "rct@thompsonclan.org")) (:maintainer "Ryan C. Thompson" . "rct@thompsonclan.org") (:keywords "completion" "minibuffer" "multiple elements") (:url . "https://github.com/DarwinAwardWinner/crm-custom"))]) (crontab-mode . [(20210715 133) ((emacs (24 3))) "Major mode for crontab(5)" tar ((:commit . "7412f3df0958812bfcacd5875a409fa795fa8ecc") (:authors ("Mario Rodas" . "marsam@users.noreply.github.com")) (:maintainers ("Mario Rodas" . "marsam@users.noreply.github.com")) (:maintainer "Mario Rodas" . "marsam@users.noreply.github.com") (:keywords "languages") (:url . "https://github.com/emacs-pe/crontab-mode"))]) (crossword . [(20210614 633) ((emacs (26 1))) "Download and play crossword puzzles" tar ((:commit . "e462de8ef15d1f979207a95b224e68d7feead92f") (:keywords "games") (:url . "https://github.com/Boruch-Baum/emacs-crossword"))]) - (crux . [(20240229 957) ((emacs (26 1))) "A Collection of Ridiculously Useful eXtensions" tar ((:commit . "7980df10e47eef41d4d1c57cfb690ec406381ed3") (:authors ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainers ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "convenience") (:url . "https://github.com/bbatsov/crux"))]) + (crux . [(20240401 1136) ((emacs (26 1))) "A Collection of Ridiculously Useful eXtensions" tar ((:commit . "6ed75a69f542fb7feab6b8f182caf0924b3fb510") (:authors ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainers ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "convenience") (:url . "https://github.com/bbatsov/crux"))]) (cryptol-mode . [(20190531 2051) nil "Cryptol major mode for Emacs" tar ((:commit . "81ebbde83f7cb75b2dfaefc09de6a1703068c769") (:authors (nil . "Austin Seipp <aseipp [@at] pobox [dot] com>")) (:maintainers (nil . "Austin Seipp <aseipp [@at] pobox [dot] com>")) (:maintainer nil . "Austin Seipp <aseipp [@at] pobox [dot] com>") (:keywords "cryptol" "cryptography") (:url . "http://github.com/thoughtpolice/cryptol-mode"))]) (crystal-mode . [(20231205 1943) ((emacs (24 4))) "Major mode for editing Crystal files" tar ((:commit . "ea89b108fa4222df94ffb99e6e7eaec5d7aa4fea") (:keywords "languages" "crystal") (:url . "https://github.com/crystal-lang-tools/emacs-crystal-mode"))]) (crystal-playground . [(20180830 501) ((emacs (25)) (crystal-mode (0 1 2))) "Local crystal playground for short code snippets." tar ((:commit . "532dc7e4239eb4bdd241bc4347d34760344c1ebb") (:authors ("Jason Howell")) (:maintainers ("Jason Howell")) (:maintainer "Jason Howell") (:keywords "tools" "crystal") (:url . "https://github.com/jasonrobot/crystal-playground"))]) @@ -915,7 +915,7 @@ (daml-mode . [(20231106 916) ((emacs (27 1)) (haskell-mode (16 1))) "Major mode for daml" tar ((:commit . "3ba1166edd4c22402996625b1f8a05a2d5b1cbc6") (:authors ("Bártfai Tamás")) (:maintainers ("Bártfai Tamás")) (:maintainer "Bártfai Tamás") (:url . "https://github.com/bartfaitamas/daml-mode"))]) (danneskjold-theme . [(20231110 722) nil "Beautiful high-contrast Emacs theme." tar ((:commit . "b3335e44f468c019c95a8210ce1ed9fe23c65735") (:authors ("Dmitry Akatov" . "akatovda@yandex.com")) (:maintainer "Dmitry Akatov" . "akatovda@yandex.com") (:url . "https://github.com/rails-to-cosmos/danneskjold-theme"))]) (dante . [(20230808 658) ((dash (2 12 0)) (emacs (27 1)) (f (0 19 0)) (flycheck (0 30)) (company (0 9)) (flymake (1 0)) (s (1 11 0)) (lcr (1 5))) "Development mode for Haskell" tar ((:commit . "ca47f8cc1392c7045db7da8b4fafe86b7c044e90") (:authors ("Jean-Philippe Bernardy" . "jeanphilippe.bernardy@gmail.com")) (:maintainers ("Jean-Philippe Bernardy" . "jeanphilippe.bernardy@gmail.com")) (:maintainer "Jean-Philippe Bernardy" . "jeanphilippe.bernardy@gmail.com") (:keywords "haskell" "tools") (:url . "https://github.com/jyp/dante"))]) - (dap-mode . [(20240113 926) ((emacs (27 1)) (dash (2 18 0)) (lsp-mode (6 0)) (bui (1 1 0)) (f (0 20 0)) (s (1 12 0)) (lsp-treemacs (0 1)) (posframe (0 7 0)) (ht (2 3)) (lsp-docker (1 0 0))) "Debug Adapter Protocol mode" tar ((:commit . "03bcfe90b6acb6811bfa877882ba72949503234d") (:authors ("Ivan Yonchovski" . "yyoncho@gmail.com")) (:maintainers ("Ivan Yonchovski" . "yyoncho@gmail.com")) (:maintainer "Ivan Yonchovski" . "yyoncho@gmail.com") (:keywords "languages" "debug") (:url . "https://github.com/emacs-lsp/dap-mode"))]) + (dap-mode . [(20240403 2257) ((emacs (27 1)) (dash (2 18 0)) (lsp-mode (6 0)) (bui (1 1 0)) (f (0 20 0)) (s (1 12 0)) (lsp-treemacs (0 1)) (posframe (0 7 0)) (ht (2 3)) (lsp-docker (1 0 0))) "Debug Adapter Protocol mode" tar ((:commit . "ed360fda01c75d7493d48ae750d4786fa78a34e3") (:authors ("Ivan Yonchovski" . "yyoncho@gmail.com")) (:maintainers ("Ivan Yonchovski" . "yyoncho@gmail.com")) (:maintainer "Ivan Yonchovski" . "yyoncho@gmail.com") (:keywords "languages" "debug") (:url . "https://github.com/emacs-lsp/dap-mode"))]) (darcsum . [(20190316 2215) nil "a pcl-cvs like interface for managing darcs patches" tar ((:commit . "6a8b690539d133c5e3d17cb23fe4365fbb6fb493") (:authors ("John Wiegley" . "johnw@gnu.org")) (:maintainers ("John Wiegley" . "johnw@gnu.org")) (:maintainer "John Wiegley" . "johnw@gnu.org") (:keywords "completion" "convenience" "tools" "vc"))]) (darcula-theme . [(20171227 1845) nil "Inspired by IntelliJ's Darcula theme" tar ((:commit . "d9b82b58ded9014985be6658f4ab17e26ed9e93e") (:authors ("Sam Halliday" . "Sam.Halliday@gmail.com")) (:maintainers ("Sam Halliday" . "Sam.Halliday@gmail.com")) (:maintainer "Sam Halliday" . "Sam.Halliday@gmail.com") (:keywords "faces") (:url . "https://gitlab.com/fommil/emacs-darcula-theme"))]) (dark-krystal-theme . [(20170808 1300) ((emacs (24 0))) "an Emacs 24 theme based on Dark Krystal (tmTheme)" tar ((:commit . "79084b99665dc9ffb0ec62cc092349a5ecebebbc") (:authors ("Jason Milkins")) (:maintainers ("Jason Milkins")) (:maintainer "Jason Milkins") (:url . "https://github.com/emacsfodder/tmtheme-to-deftheme"))]) @@ -998,7 +998,7 @@ (didyoumean . [(20240229 1807) ((emacs (24 4))) "Did you mean to open another file?" tar ((:commit . "fc12bd33c7b4f6dc74e49735c269ff75c72227a1") (:keywords "convenience") (:url . "https://gitlab.com/kisaragi-hiu/didyoumean.el"))]) (diff-ansi . [(20231221 57) ((emacs (27 1))) "Display diff's using alternative diffing tools" tar ((:commit . "cdae72a4713704692aa4edf3433d6b6df1d84746") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainers ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-diff-ansi"))]) (diff-at-point . [(20230320 2355) ((emacs (26 2))) "Diff navigation" tar ((:commit . "0a4815a364b636eadf2f9ca6f468fb5996ff8d6f") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainers ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-diff-at-point"))]) - (diff-hl . [(20240225 116) ((cl-lib (0 2)) (emacs (25 1))) "Highlight uncommitted changes using VC" tar ((:commit . "96620839430c1205cbb8c92dd54973397f70f9d2") (:authors ("Dmitry Gutov" . "dmitry@gutov.dev")) (:maintainers ("Dmitry Gutov" . "dmitry@gutov.dev")) (:maintainer "Dmitry Gutov" . "dmitry@gutov.dev") (:keywords "vc" "diff") (:url . "https://github.com/dgutov/diff-hl"))]) + (diff-hl . [(20240401 2357) ((cl-lib (0 2)) (emacs (25 1))) "Highlight uncommitted changes using VC" tar ((:commit . "09a0f8fcc7e6ce3f49dca64cdfab898d6762f2a4") (:authors ("Dmitry Gutov" . "dmitry@gutov.dev")) (:maintainers ("Dmitry Gutov" . "dmitry@gutov.dev")) (:maintainer "Dmitry Gutov" . "dmitry@gutov.dev") (:keywords "vc" "diff") (:url . "https://github.com/dgutov/diff-hl"))]) (diffed . [(20230208 1546) ((emacs (27 1))) "Diffed is for recursive diff like Dired is for ls" tar ((:commit . "f7dc37f13a4f1660212c41a6e9faba61eb8cc078") (:authors ("Bernhard Rotter" . "bernhard@b-rotter.de")) (:maintainers ("Bernhard Rotter" . "bernhard@b-rotter.de")) (:maintainer "Bernhard Rotter" . "bernhard@b-rotter.de") (:keywords "tools") (:url . "https://github.com/ber-ro/diffed"))]) (difflib . [(20210224 2242) ((emacs (24 4)) (cl-generic (0 3)) (ht (2 2)) (s (1 12 0))) "Helpers for computing deltas between sequences." tar ((:commit . "646fc4388274fe765bbf4661e17a24e4d081250c") (:authors ("Diego A. Mundo" . "dieggsy@pm.me")) (:maintainers ("Diego A. Mundo" . "dieggsy@pm.me")) (:maintainer "Diego A. Mundo" . "dieggsy@pm.me") (:keywords "matching" "tools" "string") (:url . "http://github.com/dieggsy/difflib.el"))]) (diffpdf . [(20210626 1447) ((emacs (25 1)) (transient (0 3 0))) "Transient diffpdf" tar ((:commit . "a5b203b549e373cb9b0ef3f00c0010bd34dd644a") (: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/diffpdf.el"))]) @@ -1134,7 +1134,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 . [(20240303 1522) nil "startup file for the elpa-dpkg-dev-el package" tar ((:commit . "69b294694c57d8e7571fada6a6651c40d266285e") (:authors ("Peter S Galbraith" . "psg@debian.org")) (:maintainers ("Peter S Galbraith" . "psg@debian.org")) (:maintainer "Peter S Galbraith" . "psg@debian.org"))]) + (dpkg-dev-el . [(20240404 956) nil "startup file for the elpa-dpkg-dev-el package" tar ((:commit . "b9d0223da04ed56b5f90210237b5c71afd03076e") (: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"))]) @@ -1164,7 +1164,7 @@ (dut-mode . [(20170729 2111) ((emacs (24))) "Major mode for the Dut programming language" tar ((:commit . "9235c7acaa6690942e9de8b7acd1e4be0c859dc1") (:authors ("The dut-mode Authors")) (:maintainers ("The dut-mode Authors")) (:maintainer "The dut-mode Authors") (:keywords "languages" "gut") (:url . "https://github.com/dut-lang/dut-mode"))]) (dw . [(20210331 2311) ((emacs (25 1))) "Diceware passphrase generation commands" tar ((:commit . "61c5718ba64ace4c9e29de18aa2690ecc3f0f258") (:authors ("D. Williams" . "d.williams@posteo.net")) (:maintainers ("D. Williams" . "d.williams@posteo.net")) (:maintainer "D. Williams" . "d.williams@posteo.net") (:keywords "convenience" "games") (:url . "https://github.com/integral-dw/dw-passphrase-generator"))]) (dwim-coder-mode . [(20230830 1215) ((emacs (29))) "DWIM keybindings for C, Python, Rust, and more" tar ((:commit . "94a752fca078144dd309343880abafbc4eacca5f") (:authors ("Mohammed Sadiq" . "sadiq@sadiqpk.org")) (:maintainers ("Mohammed Sadiq" . "sadiq@sadiqpk.org")) (:maintainer "Mohammed Sadiq" . "sadiq@sadiqpk.org") (:keywords "convenience" "hacks") (:url . "https://sadiqpk.org/projects/dwim-coder-mode.html"))]) - (dwim-shell-command . [(20240324 1906) ((emacs (28 1))) "Shell commands with DWIM behaviour" tar ((:commit . "5049375dcba5b58c35b558c9a5975c3424855ee6") (:authors ("Alvaro Ramirez")) (:maintainers ("Alvaro Ramirez")) (:maintainer "Alvaro Ramirez") (:url . "https://github.com/xenodium/dwim-shell-command"))]) + (dwim-shell-command . [(20240402 2119) ((emacs (28 1))) "Shell commands with DWIM behaviour" tar ((:commit . "81f0d77a472a6445f2b732b12211d6b510ed0a12") (:authors ("Alvaro Ramirez")) (:maintainers ("Alvaro Ramirez")) (:maintainer "Alvaro Ramirez") (:url . "https://github.com/xenodium/dwim-shell-command"))]) (dyalog-mode . [(20230214 1027) ((cl-lib (0 2)) (emacs (24 3))) "Major mode for editing Dyalog APL source code" tar ((:commit . "13c0d391aa878a1609259a89fe3e6db8d21935e8") (:authors ("Joakim Hårsman" . "joakim.harsman@gmail.com")) (:maintainers ("Joakim Hårsman" . "joakim.harsman@gmail.com")) (:maintainer "Joakim Hårsman" . "joakim.harsman@gmail.com") (:keywords "languages") (:url . "https://github.com/harsman/dyalog-mode.git"))]) (dylan . [(20220115 1804) ((emacs (25 1))) "Dylan editing modes" tar ((:commit . "9d2891e3e06405b75072d296f385fa795aeb9835") (:url . "https://opendylan.org/"))]) (dynamic-fonts . [(20140731 1226) ((font-utils (0 7 0)) (persistent-soft (0 8 8)) (pcache (0 2 3))) "Set faces based on available fonts" tar ((:commit . "004ee6014dc7dbff8f14d26015c91d9229f6eac0") (:authors ("Roland Walker" . "walker@pobox.com")) (:maintainers ("Roland Walker" . "walker@pobox.com")) (:maintainer "Roland Walker" . "walker@pobox.com") (:keywords "faces" "frames") (:url . "http://github.com/rolandwalker/dynamic-fonts"))]) @@ -1241,7 +1241,7 @@ (eglot-fsharp . [(20230324 1942) ((emacs (27 1)) (eglot (1 4)) (fsharp-mode (1 10)) (jsonrpc (1 0 14))) "fsharp-mode eglot integration" tar ((:commit . "0ce19f272949f9ed335ab7a9fd9454c01f07eb8f") (:authors ("Jürgen Hötzel" . "juergen@hoetzel.info")) (:maintainers ("Jürgen Hötzel" . "juergen@hoetzel.info")) (:maintainer "Jürgen Hötzel" . "juergen@hoetzel.info") (:keywords "languages") (:url . "https://github.com/fsharp/emacs-fsharp-mode"))]) (eglot-java . [(20240322 239) ((emacs (26 1)) (eglot (1 0)) (jsonrpc (1 0 0))) "Java extension for the eglot LSP client" tar ((:commit . "d09a4204ece16a5be4ddea71476ef87e565733bd") (:authors ("Yves Zoundi" . "yves_zoundi@hotmail.com")) (:maintainers ("Yves Zoundi" . "yves_zoundi@hotmail.com")) (:maintainer "Yves Zoundi" . "yves_zoundi@hotmail.com") (:keywords "convenience" "languages") (:url . "https://github.com/yveszoundi/eglot-java"))]) (eglot-jl . [(20240318 1159) ((emacs (25 1)) (eglot (1 4)) (project (0 8 1)) (cl-generic (1 0))) "Julia support for eglot" tar ((:commit . "1d9cab682380f37ca1e9e9933cda13164600706d") (:authors ("Adam Beckmeyer" . "adam_git@thebeckmeyers.xyz")) (:maintainers ("Adam Beckmeyer" . "adam_git@thebeckmeyers.xyz")) (:maintainer "Adam Beckmeyer" . "adam_git@thebeckmeyers.xyz") (:keywords "convenience" "languages") (:url . "https://github.com/non-Jedi/eglot-jl"))]) - (eglot-luau . [(20240330 42) ((emacs (29 1))) "Luau language server integration for eglot" tar ((:commit . "9ce68bfee3d7270237c0f6f0ac0a147b33c1b8d8") (:authors ("Kenneth Loeffler" . "kenloef@gmail.com")) (:maintainers ("Kenneth Loeffler" . "kenloef@gmail.com")) (:maintainer "Kenneth Loeffler" . "kenloef@gmail.com") (:keywords "roblox" "luau" "tools") (:url . "https://github.com/kennethloeffler/eglot-luau"))]) + (eglot-luau . [(20240401 2209) ((emacs (29 1))) "Luau language server integration for eglot" tar ((:commit . "3926860036402cce4a55faec534b88c0bf6006fd") (:authors ("Kenneth Loeffler" . "kenloef@gmail.com")) (:maintainers ("Kenneth Loeffler" . "kenloef@gmail.com")) (:maintainer "Kenneth Loeffler" . "kenloef@gmail.com") (:keywords "roblox" "luau" "tools") (:url . "https://github.com/kennethloeffler/eglot-luau"))]) (eglot-signature-eldoc-talkative . [(20240209 2034) ((emacs (29 1)) (eglot (1 16)) (eldoc (1 14 0)) (jsonrpc (1 0 23))) "Make Eglot make ElDoc echo docs" tar ((:commit . "b6604648a2ea5d260fa315ffbaebebbf3872343a") (:authors ("João Távora" . "joaotavora@gmail.com") ("Mekeor Melire" . "mekeor@posteo.de")) (:maintainers ("Mekeor Melire" . "mekeor@posteo.de")) (:maintainer "Mekeor Melire" . "mekeor@posteo.de") (:keywords "convenience" "documentation" "eglot" "eldoc" "languages" "lsp") (:url . "https://codeberg.org/mekeor/emacs-eglot-signature-eldoc-talkative"))]) (eglot-tempel . [(20230626 1004) ((eglot (1 9)) (tempel (0 5)) (emacs (24 4))) "Use eglot as inline template expander" tar ((:commit . "70fee6ac33df7a6c5cce967005766b6702a9e9a5") (:authors ("Jeff Walsh" . "fejfighter@gmail.com")) (:maintainers ("Jeff Walsh" . "fejfighter@gmail.com")) (:maintainer "Jeff Walsh" . "fejfighter@gmail.com") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/fejfighter/eglot-tempel"))]) (ego . [(20200803 1101) ((emacs (24 5)) (ht (1 5)) (mustache (0 22)) (htmlize (1 47)) (org (8 0)) (dash (2 0 0))) "a static site generator based on org mode, forked from org-page." tar ((:commit . "211c4cb2af2582849d9df984fb2346deecaf79be") (:authors ("Feng Shu <tumashu AT 163.com>") ("Kelvin Hu <ini DOT kelvin AT gmail DOT com>") ("Kuangdash <kuangdash AT 163.com>")) (:maintainer "Feng Shu <tumashu AT 163.com>") (:keywords "org-mode" "convenience" "beautify") (:url . "https://github.com/emacs-china/EGO"))]) @@ -1308,7 +1308,7 @@ (elforth . [(20210522 928) ((emacs (26 1))) "Do you have what it takes to hack Emacs Lisp in Forth?" tar ((:commit . "2d8540434a28e7edaa04a992c3c362832b2fd61e") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainers ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "games") (:url . "https://github.com/lassik/elforth"))]) (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 . [(20240322 1808) ((emacs (29 2)) (ellama (0 8 6)) (llm (0 9 1)) (async (1 9 8))) "Emacs Lisp Information System Assistant" tar ((:commit . "8794e14d75998fd20ab6d9c52ff4c44251428559") (: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"))]) + (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 . [(20231207 1018) ((emacs (29 1))) "Emacs lisp auto-format" tar ((:commit . "6b280c854f964c303de2f9316f8f955e52893b8d") (: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"))]) @@ -1323,7 +1323,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 . [(20240316 1610) ((emacs (28 1)) (llm (0 6 0)) (spinner (1 7 4))) "Tool for interacting with LLMs" tar ((:commit . "b94d5952d4e36e1caeef5ac0b3ecd6d89009082b") (: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 . [(20240403 1714) ((emacs (28 1)) (llm (0 6 0)) (spinner (1 7 4))) "Tool for interacting with LLMs" tar ((:commit . "a931224a0d096938f912da7a125fbd569cec6251") (: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"))]) @@ -1461,7 +1461,7 @@ (esh-autosuggest . [(20210906 1446) ((emacs (24 4)) (company (0 9 4))) "History autosuggestions for eshell" tar ((:commit . "bf676b137d35553debe32ff134dbec25f3978ae7") (:authors ("Diego A. Mundo" . "dieggsy@pm.me")) (:maintainers ("Diego A. Mundo" . "dieggsy@pm.me")) (:maintainer "Diego A. Mundo" . "dieggsy@pm.me") (:keywords "completion" "company" "matching" "convenience" "abbrev") (:url . "http://github.com/dieggsy/esh-autosuggest"))]) (esh-buf-stack . [(20140107 1018) nil "Add a buffer stack feature to Eshell" tar ((:commit . "ea5da9ce8566ffe2e013f0e588701cb0825258b6") (:authors ("Tomoya Tanjo" . "ttanjo@gmail.com")) (:maintainers ("Tomoya Tanjo" . "ttanjo@gmail.com")) (:maintainer "Tomoya Tanjo" . "ttanjo@gmail.com") (:keywords "eshell" "extensions"))]) (esh-help . [(20190905 22) ((dash (1 4 0))) "Add some help functions and support for Eshell" tar ((:commit . "417673ed18a983930a66a6692dbfb288a995cb80") (:authors ("Tomoya Tanjo" . "ttanjo@gmail.com")) (:maintainers ("Tomoya Tanjo" . "ttanjo@gmail.com")) (:maintainer "Tomoya Tanjo" . "ttanjo@gmail.com") (:keywords "eshell" "extensions") (:url . "https://github.com/tom-tan/esh-help/"))]) - (eshell-atuin . [(20240312 1546) ((emacs (27 1)) (compat (29 1 4 1))) "Intergrate eshell with atuin, a shell history tool" tar ((:commit . "24ac9fac36ef8a7919e081d22d5a7fa5428a01ba") (:authors ("Korytov Pavel" . "thexcloud@gmail.com")) (:maintainers ("Korytov Pavel" . "thexcloud@gmail.com")) (:maintainer "Korytov Pavel" . "thexcloud@gmail.com") (:url . "https://github.com/SqrtMinusOne/eshell-atuin.el"))]) + (eshell-atuin . [(20240401 1414) ((emacs (27 1)) (compat (29 1 4 1))) "Integrate eshell with atuin, a shell history tool" tar ((:commit . "0731963667979691ec5fbe704e7038a6d5587abd") (:authors ("Korytov Pavel" . "thexcloud@gmail.com")) (:maintainers ("Korytov Pavel" . "thexcloud@gmail.com")) (:maintainer "Korytov Pavel" . "thexcloud@gmail.com") (:url . "https://github.com/SqrtMinusOne/eshell-atuin"))]) (eshell-autojump . [(20201117 235) nil "autojump command for Eshell" tar ((:commit . "c1056bfc6b46646ae1e606247689fef9aee621af") (:authors ("Alex Schroeder")) (:maintainers ("Yen-Chin, Lee" . "coldnew.tw@gmail.com")) (:maintainer "Yen-Chin, Lee" . "coldnew.tw@gmail.com") (:url . "http://github.com/coldnew/eshell-autojump"))]) (eshell-bookmark . [(20170922 1514) ((emacs (24 3))) "Integrate bookmarks with eshell." tar ((:commit . "deda4b848b2fb979dbe73ead2cb866610e3596ed") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainers ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:keywords "convenience" "files") (:url . "https://github.com/Fuco1/eshell-bookmark"))]) (eshell-did-you-mean . [(20211104 237) ((emacs (24 1)) (cl-lib (0 5))) "command not found (\"did you mean…\" feature) in Eshell" tar ((:commit . "80cd8c4b186a2fb29621cf634bcf2bcd914f1e3d") (:authors ("Chunyang Xu" . "xuchunyang56@gmail.com")) (:maintainers ("Chunyang Xu" . "xuchunyang56@gmail.com")) (:maintainer "Chunyang Xu" . "xuchunyang56@gmail.com") (:keywords "eshell") (:url . "https://github.com/xuchunyang/eshell-did-you-mean"))]) @@ -1498,7 +1498,6 @@ (esup . [(20220202 2335) ((cl-lib (0 5)) (s (1 2)) (emacs (25 1))) "The Emacs StartUp Profiler (ESUP)" tar ((:commit . "4b49c8d599d4cc0fbf994e9e54a9c78e5ab62a5f") (:authors ("Joe Schafer" . "joe@jschaf.com")) (:maintainers ("Serghei Iakovlev" . "egrep@protonmail.ch")) (:maintainer "Serghei Iakovlev" . "egrep@protonmail.ch") (:keywords "convenience" "processes") (:url . "https://github.com/jschaf/esup"))]) (esxml . [(20230308 2254) ((emacs (24 1)) (kv (0 0 5)) (cl-lib (0 5))) "Library for working with xml via esxml and sxml" tar ((:commit . "225693096a587492d76bf696d1f0c25c61f7d531") (:authors ("Vanya Izaksonas-Smith <izak0002 at umn dot edu>")) (:maintainer "Vanya Izaksonas-Smith") (:keywords "tools" "lisp" "comm") (:url . "https://github.com/tali713/esxml"))]) (eta . [(20210115 1655) ((emacs (25 1)) (ht (2 2)) (dash (2 17))) "standard and multi dispatch key bind" tar ((:commit . "651f96c46eeb7ff8a0f0efcfacad5b4d25bfaa4b") (:authors ("Chris Zheng")) (:maintainers ("Chris Zheng")) (:maintainer "Chris Zheng") (:keywords "convenience" "usability") (:url . "https://www.github.com/zcaudate/eta"))]) - (etable . [(20161028 2009) ((dash (2 9 0)) (interval-list (0 1)) (emacs (24 4))) "Implementation of javax.swing.JTable for Emacs." tar ((:commit . "d502141f0c69bf95256ba5cb9cd15350c7e942d2") (:authors ("Matus Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matus Goljer" . "matus.goljer@gmail.com") (:keywords "convenience") (:url . "https://github.com/Fuco1/ETable"))]) (etc-sudoers-mode . [(20201102 1707) ((sudo-edit (0)) (with-editor (0))) "Edit Sudo security policies" tar ((:commit . "74c66c58c9578a0d841206d5dec04d81e7b3d741") (:authors ("Peter Oliver" . "git@mavit.org.uk")) (:maintainers ("Peter Oliver" . "git@mavit.org.uk")) (:maintainer "Peter Oliver" . "git@mavit.org.uk") (:keywords "languages") (:url . "https://gitlab.com/mavit/etc-sudoers-mode/"))]) (etd . [(20230711 547) ((emacs (24 4))) "Examples to Tests and Docs" tar ((:commit . "65f713935c9d2598f6fa4674bf2bdac2169005a9") (:authors ("Jason M23" . "jasonm23@gmail.com")) (:maintainers ("Jason M23" . "jasonm23@gmail.com")) (:maintainer "Jason M23" . "jasonm23@gmail.com") (:keywords "lisp" "tools" "extensions") (:url . "https://github.com/emacsfodder/kurecolor"))]) (eterm-256color . [(20210224 2241) ((emacs (24 4)) (xterm-color (1 7)) (f (0 19 0))) "Customizable 256 colors for term." tar ((:commit . "05fdbd336a888a0f4068578a6d385d8bf812a4e8") (:authors ("Diego A. Mundo" . "dieggsy@pm.me")) (:maintainers ("Diego A. Mundo" . "dieggsy@pm.me")) (:maintainer "Diego A. Mundo" . "dieggsy@pm.me") (:keywords "faces") (:url . "http://github.com/dieggsy/eterm-256color"))]) @@ -1513,7 +1512,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 . [(20230105 507) ((emacs (25 1))) "The *scratch* that lasts forever" tar ((:commit . "1b7dac779501dcd988552aa6455a5be89e8b0562") (: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 . [(20240331 2216) ((emacs (24 1)) (goto-chg (1 6)) (cl-lib (0 5))) "Extensible Vi layer for Emacs." tar ((:commit . "004ac4e0cd766d49d48d53270e9c0c080ad9f173") (:maintainer "Tom Dalziel" . "tom.dalziel@gmail.com") (:keywords "emulations") (:url . "https://github.com/emacs-evil/evil"))]) + (evil . [(20240402 1738) ((emacs (24 1)) (goto-chg (1 6)) (cl-lib (0 5))) "Extensible Vi layer for Emacs." tar ((:commit . "88d073c9d03ca223564e7e8589f44ecc87c98153") (: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"))]) @@ -1521,7 +1520,7 @@ (evil-cleverparens . [(20240308 751) ((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 . "aa19ed6fec73c883442fb4ffd8d300355d5a8580") (: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 . [(20240321 13) ((emacs (26 3)) (evil (1 2 13)) (annalist (1 0))) "A set of keybindings for Evil mode" tar ((:commit . "89aea406334bc251adb70b1d6070b24fa94552ba") (: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 . [(20240402 1844) ((emacs (26 3)) (evil (1 2 13)) (annalist (1 0))) "A set of keybindings for Evil mode" tar ((:commit . "e0982fcbb6f1694b27074565553fac0e736a30b4") (: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"))]) @@ -1615,7 +1614,7 @@ (extend-dnd . [(20151122 1850) nil "R drag and Drop" tar ((:commit . "80c966c93b82c9bb5c6225a432557c39144fc602") (:authors ("Matthew L. Fidler")) (:maintainers ("Matthew L. Fidler")) (:maintainer "Matthew L. Fidler") (:keywords "extend" "drag and drop") (:url . "https://github.com/mlf176f2/extend-dnd"))]) (external-dict . [(20231129 1043) ((emacs (25 1))) "Query external dictionary like goldendict, Bob.app etc" tar ((:commit . "17807ac7e671b44da495e7e3fac5b8cb72a25feb") (:keywords "wp" "processes") (:url . "https://repo.or.cz/external-dict.el.git"))]) (extmap . [(20230907 1959) ((emacs (24 4))) "Externally-stored constant mapping for Elisp" tar ((:commit . "3b0f898057082a1c01584ff2bbaf5fd4d22c1400") (:authors ("Paul Pogonyshev" . "pogonyshev@gmail.com")) (:maintainers ("Paul Pogonyshev" . "pogonyshev@gmail.com")) (:maintainer "Paul Pogonyshev" . "pogonyshev@gmail.com") (:keywords "lisp") (:url . "https://github.com/doublep/extmap"))]) - (exunit . [(20240303 1106) ((s (1 11 0)) (emacs (24 3)) (f (0 20 0)) (transient (0 3 6))) "ExUnit test runner" tar ((:commit . "5e8f6b681d4745ef23bd8f7ddafba80d6285f26e") (:authors ("Anantha kumaran" . "ananthakumaran@gmail.com")) (:maintainers ("Anantha kumaran" . "ananthakumaran@gmail.com")) (:maintainer "Anantha kumaran" . "ananthakumaran@gmail.com") (:keywords "processes" "elixir" "exunit") (:url . "http://github.com/ananthakumaran/exunit.el"))]) + (exunit . [(20240401 1344) ((s (1 11 0)) (emacs (24 3)) (f (0 20 0)) (transient (0 3 6))) "ExUnit test runner" tar ((:commit . "3b63d8835398ccadbcec080c8c1e00cd638b62b0") (:authors ("Anantha kumaran" . "ananthakumaran@gmail.com")) (:maintainers ("Anantha kumaran" . "ananthakumaran@gmail.com")) (:maintainer "Anantha kumaran" . "ananthakumaran@gmail.com") (:keywords "processes" "elixir" "exunit") (:url . "http://github.com/ananthakumaran/exunit.el"))]) (exwm-edit . [(20240221 124) ((emacs (27 1))) "Edit mode for EXWM" tar ((:commit . "19ce910a9183aac78365d674cbeb630464ff1836") (:authors ("Ag Ibragimov")) (:maintainers ("Ag Ibragimov")) (:maintainer "Ag Ibragimov") (:keywords "convenience") (:url . "https://github.com/agzam/exwm-edit"))]) (exwm-firefox-core . [(20190812 2110) ((emacs (24 4)) (exwm (0 16))) "Firefox hotkeys to functions" tar ((:commit . "e2fe2a895e8f973307ef52f8c9976b26e701cbd0") (: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 "extensions") (:url . "https://github.com/walseb/exwm-firefox-core"))]) (exwm-firefox-evil . [(20231026 309) ((emacs (24 4)) (exwm (0 16)) (evil (1 0 0)) (exwm-firefox-core (1 0))) "evil-mode implementation of exwm-firefox-core" tar ((:commit . "ec9e14eca25aea9b7c7169be23843898f46696e7") (: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 "extensions") (:url . "https://github.com/walseb/exwm-firefox-evil"))]) @@ -1659,7 +1658,7 @@ (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"))]) (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 . [(20230315 1821) ((emacs (28 1))) "A modeline with very little" tar ((:commit . "3f9247f48058285d3e03957680e011ecf58d6feb") (:authors ("chee" . "emacs@chee.party")) (:maintainers ("chee" . "emacs@chee.party")) (:maintainer "chee" . "emacs@chee.party") (:url . "https://opensource.chee.party/chee/feline-mode"))]) + (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"))]) (fennel-mode . [(20240313 32) ((emacs (26 1))) "A major-mode for editing Fennel code" tar ((:commit . "4cdf20b673ed1eb14342d7a470ef75cbe165b0c4") (:authors ("Phil Hagelberg")) (:maintainers ("Phil Hagelberg")) (:maintainer "Phil Hagelberg") (:keywords "languages" "tools") (:url . "https://git.sr.ht/~technomancy/fennel-mode"))]) (fetch . [(20131201 730) nil "Fetch and unpack resources" tar ((:commit . "3f2793afcbbc32f320e572453166f9354ecc6d06") (:authors ("Christian 'crshd' Brassat" . "christian.brassat@gmail.com")) (:maintainers ("Christian 'crshd' Brassat" . "christian.brassat@gmail.com")) (:maintainer "Christian 'crshd' Brassat" . "christian.brassat@gmail.com") (:url . "https://github.com/crshd/fetch.el"))]) (ffmpeg-player . [(20240101 926) ((emacs (24 4)) (s (1 12 0)) (f (0 20 0))) "Play video using ffmpeg" tar ((:commit . "c3808dc1c39499a81e1b9463526fda924fd1f062") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "multimedia" "video" "ffmpeg" "buffering" "images") (:url . "https://github.com/jcs-elpa/ffmpeg-player"))]) @@ -1894,7 +1893,7 @@ (flymake-rakudo . [(20220424 637) ((emacs (28 1)) (flymake-collection (2 0 0)) (let-alist (1 0))) "Flymake syntax checker for Rakudo" tar ((:commit . "f8e3d03a7207876cd891174702efd572d74f2e49") (:authors ("Siavash Askari Nasr" . "ciavash@proton.me")) (:maintainers ("Siavash Askari Nasr" . "ciavash@proton.me")) (:maintainer "Siavash Askari Nasr" . "ciavash@proton.me") (:keywords "language" "tools" "convenience") (:url . "https://github.com/Raku/flymake-rakudo"))]) (flymake-relint . [(20230803 120) ((emacs (26 1)) (relint (1 23))) "A relint Flymake backend" tar ((:commit . "cded537b8208e87632e1fb5b9bdc819a736eb9a3") (:authors ("liuyinz" . "liuyinz95@gmail.com")) (:maintainers ("liuyinz" . "liuyinz95@gmail.com")) (:maintainer "liuyinz" . "liuyinz95@gmail.com") (:keywords "lisp") (:url . "https://github.com/liuyinz/flymake-relint"))]) (flymake-ruby . [(20170723 146) ((flymake-easy (0 1))) "A flymake handler for ruby-mode files" tar ((:commit . "6c320c6fb686c5223bf975cc35178ad6b195e073") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainers ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:url . "https://github.com/purcell/flymake-ruby"))]) - (flymake-ruff . [(20240113 1518) ((emacs (26 1)) (project (0 3 0))) "A flymake plugin for python files using ruff" tar ((:commit . "165ee1fe7b9d154a316c332bf73f341177126d8c") (:authors ("Erick Navarro" . "erick@navarro.io")) (:maintainers ("Erick Navarro" . "erick@navarro.io")) (:maintainer "Erick Navarro" . "erick@navarro.io") (:url . "https://github.com/erickgnavar/flymake-ruff"))]) + (flymake-ruff . [(20240401 1544) ((emacs (26 1)) (project (0 3 0))) "A flymake plugin for python files using ruff" tar ((:commit . "b7a26e5121b64804174a0abcba55e96d514acf5f") (:authors ("Erick Navarro" . "erick@navarro.io")) (:maintainers ("Erick Navarro" . "erick@navarro.io")) (:maintainer "Erick Navarro" . "erick@navarro.io") (:url . "https://github.com/erickgnavar/flymake-ruff"))]) (flymake-sass . [(20170723 146) ((flymake-easy (0 1))) "Flymake handler for sass and scss files" tar ((:commit . "2de28148e92deb93bff3d55fe14e7c67ac476056") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainers ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:url . "https://github.com/purcell/flymake-sass"))]) (flymake-shell . [(20170723 146) ((flymake-easy (0 1))) "A flymake syntax-checker for shell scripts" tar ((:commit . "a16cf453056b9849cc7c912bb127fb0b08fc6dab") (:authors ("Steve Purcell" . "steve@sanityinc.com")) (:maintainers ("Steve Purcell" . "steve@sanityinc.com")) (:maintainer "Steve Purcell" . "steve@sanityinc.com") (:url . "https://github.com/purcell/flymake-shell"))]) (flymake-shellcheck . [(20220308 2218) ((emacs (26))) "A bash/sh Flymake backend powered by ShellCheck" tar ((:commit . "1ad9acb599e6be6aac57280b7c918b0e4a0f07de") (:authors ("Federico Tedin" . "federicotedin@gmail.com")) (:maintainers ("Federico Tedin" . "federicotedin@gmail.com")) (:maintainer "Federico Tedin" . "federicotedin@gmail.com") (:url . "https://github.com/federicotdn/flymake-shellcheck"))]) @@ -1935,7 +1934,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 . [(20240328 1558) ((emacs (25 1)) (compat (29 1 4 4)) (closql (20240125)) (dash (2 19 1)) (emacsql (20240124)) (ghub (20240101)) (let-alist (1 0 6)) (magit (20240125)) (markdown-mode (2 6)) (seq (2 24)) (transient (20240201)) (yaml (0 5 5))) "Access Git forges from Magit." tar ((:commit . "2a25e8db65e939023a29e28fb40ac0576790674d") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/forge"))]) + (forge . [(20240401 2312) ((emacs (25 1)) (compat (29 1 4 4)) (closql (20240125)) (dash (2 19 1)) (emacsql (20240124)) (ghub (20240101)) (let-alist (1 0 6)) (magit (20240125)) (markdown-mode (2 6)) (seq (2 24)) (transient (20240201)) (yaml (0 5 5))) "Access Git forges from Magit." tar ((:commit . "132a9fe46855920ca783b1140d287653d0f792ca") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (: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 . [(20240205 2153) ((emacs (24 4)) (inheritenv (0 1)) (language-id (0 20))) "Auto-format C, C++, JS, Python, Ruby and 50 other languages" tar ((:commit . "1f4a69811b4b6a00c74fa2566ef731b17b9a2ed1") (: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"))]) @@ -1979,7 +1978,7 @@ (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"))]) - (futhark-mode . [(20230511 1235) ((emacs (24 3)) (cl-lib (0 5))) "major mode for editing Futhark source files" tar ((:commit . "8e830a65983c5175b0116360eddefa3ae4fea897") (:keywords "languages") (:url . "https://github.com/diku-dk/futhark-mode"))]) + (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"))]) (fuzzy-finder . [(20210906 217) ((emacs (24 4))) "Fuzzy Finder App Integration" tar ((:commit . "915a281fc8e50df84dcc205f9357e8314d60fa54") (:authors ("10sr" . "8.slashes@gmail.com")) (:maintainers ("10sr" . "8.slashes@gmail.com")) (:maintainer "10sr" . "8.slashes@gmail.com") (:keywords "matching") (:url . "https://github.com/10sr/fuzzy-finder-el"))]) @@ -2184,7 +2183,7 @@ (gpt . [(20231112 2012) ((emacs (24 4))) "Run instruction-following language models" tar ((:commit . "c1091d25db420320de4b121baba99d7ac2e23932") (:authors ("Andreas Stuhlmueller" . "andreas@ought.org")) (:maintainers ("Andreas Stuhlmueller" . "andreas@ought.org")) (:maintainer "Andreas Stuhlmueller" . "andreas@ought.org") (:keywords "gpt3" "language" "copilot" "convenience" "tools") (:url . "https://github.com/stuhlmueller/gpt.el"))]) (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 . [(20240330 1642) ((emacs (27 1)) (transient (0 4 0)) (compat (29 1 4 1))) "Interact with ChatGPT or other LLMs" tar ((:commit . "f24ec164cd3b4233c37fc8cdff4ad138c0bfaae1") (:authors ("Karthik Chikmagalur")) (:maintainers ("Karthik Chikmagalur")) (:maintainer "Karthik Chikmagalur") (:keywords "convenience") (:url . "https://github.com/karthink/gptel"))]) + (gptel . [(20240404 813) ((emacs (27 1)) (transient (0 4 0)) (compat (29 1 4 1))) "Interact with ChatGPT or other LLMs" tar ((:commit . "7b6e3c59009b97dcefafe124aab3a29663ccb088") (:authors ("Karthik Chikmagalur")) (:maintainers ("Karthik Chikmagalur")) (:maintainer "Karthik Chikmagalur") (:keywords "convenience") (:url . "https://github.com/karthink/gptel"))]) (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"))]) @@ -2277,7 +2276,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 . [(20240331 717) ((helm-core (3 9 7)) (wfnames (1 1)) (popup (0 5 3))) "Helm is an Emacs incremental and narrowing framework" tar ((:commit . "05234abf342346b76e422a24a2ca5d556a6ceb09") (: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 . [(20240404 726) ((helm-core (3 9 7)) (wfnames (1 1)) (popup (0 5 3))) "Helm is an Emacs incremental and narrowing framework" tar ((:commit . "129d7e59a42c532f83513ba8d60312fb22afcb90") (: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"))]) @@ -2311,7 +2310,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 . [(20240328 1225) ((emacs (25 1)) (async (1 9 7))) "Development files for Helm" tar ((:commit . "d5c01619cdc3d17d3478e82f3e111267a66064eb") (: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 . [(20240403 536) ((emacs (25 1)) (async (1 9 7))) "Development files for Helm" tar ((:commit . "4bfb16feba2fb4a2e0f43a0d86d7037bbe2b19f6") (: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"))]) @@ -2320,13 +2319,11 @@ (helm-describe-modes . [(20160212 518) ((helm (1 9)) (cl-lib (0 5)) (emacs (24 1))) "Helm interface to major and minor modes." tar ((:commit . "d2253c7c2bf4f28b9ff8a2d281bd7527c0106527") (:authors ("Tianxiang Xiong" . "tianxiang.xiong@gmail.com")) (:maintainers ("Tianxiang Xiong" . "tianxiang.xiong@gmail.com")) (:maintainer "Tianxiang Xiong" . "tianxiang.xiong@gmail.com") (:keywords "docs" "convenience") (:url . "https://github.com/emacs-helm/helm-describe-modes"))]) (helm-dictionary . [(20230922 1111) ((helm (1 5 5))) "Helm source for looking up dictionaries" tar ((:commit . "fc1c097cc53dd3451bfb49ea7e99fdfc6d93bc16") (:authors ("Titus von der Malsburg" . "malsburg@posteo.de") ("Michael Heerdegen" . "michael_heerdegen@web.de")) (:maintainers ("Titus von der Malsburg" . "malsburg@posteo.de")) (:maintainer "Titus von der Malsburg" . "malsburg@posteo.de") (:url . "https://github.com/emacs-helm/helm-dictionary"))]) (helm-directory . [(20170709 1103) ((emacs (24 4)) (helm (2 0))) "selecting directory before select the file" tar ((:commit . "2c6d45404506ba744888dcdb65e9f63878f2da16") (:authors ("Masashı Mıyaura")) (:maintainers ("Masashı Mıyaura")) (:maintainer "Masashı Mıyaura") (:url . "https://github.com/masasam/emacs-helm-directory"))]) - (helm-dired-history . [(20170524 1046) ((helm (1 9 8)) (cl-lib (0 5))) "Show dired history with helm.el support." tar ((:commit . "281523f9fc46cf00fafd670ba5cd16552a607212") (:authors ("Joseph(纪秀峰)" . "jixiuf@gmail.com")) (:maintainers ("Joseph(纪秀峰)" . "jixiuf@gmail.com")) (:maintainer "Joseph(纪秀峰)" . "jixiuf@gmail.com") (:keywords "helm" "dired history") (:url . "https://github.com/jixiuf/helm-dired-history"))]) (helm-dired-recent-dirs . [(20131228 1414) ((helm (1 0))) "Show recent dirs with helm.el support." tar ((:commit . "3bcd125b44f5a707588ae3868777d91192351523") (:authors ("Akisute" . "akisute3@gmail.com")) (:maintainers ("Akisute" . "akisute3@gmail.com")) (:maintainer "Akisute" . "akisute3@gmail.com") (:keywords "helm" "dired" "zsh"))]) (helm-dogears . [(20230608 356) ((emacs (26 3)) (dogears (0 1 -1)) (helm (3 6))) "Helm source for Dogears" tar ((:commit . "7ba83bd8924cec66fe3ede3334e98b1845e6852e") (: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/dogears.el"))]) (helm-emmet . [(20160713 1231) ((helm (1 0)) (emmet-mode (1 0 2))) "helm sources for emmet-mode's snippets" tar ((:commit . "f0364e736b10cf44232053a78de04133a88185ae") (:authors ("Yasuyuki Oka" . "yasuyk@gmail.com")) (:maintainers ("Yasuyuki Oka" . "yasuyk@gmail.com")) (:maintainer "Yasuyuki Oka" . "yasuyk@gmail.com") (:keywords "convenience" "helm" "emmet") (:url . "https://github.com/yasuyk/helm-emmet"))]) (helm-emms . [(20220314 1633) ((helm (1 5)) (emms (6 0)) (cl-lib (0 5)) (emacs (24 1))) "Emms for Helm." tar ((:commit . "aefa44ab77808626c4951be2df49a2eab7820805") (:authors ("Thierry Volpiatto" . "thierry.volpiatto@gmail.com")) (:maintainers ("Thierry Volpiatto" . "thierry.volpiatto@gmail.com")) (:maintainer "Thierry Volpiatto" . "thierry.volpiatto@gmail.com") (:keywords "multimedia" "emms") (:url . "https://github.com/emacs-helm/helm-emms"))]) (helm-esa . [(20190721 1429) ((emacs (26 2)) (helm (3 2)) (request (0 3 0))) "Esa with helm interface" tar ((:commit . "d93b4af404346870cb2cf9c257d055332ef3f577") (:authors ("Takashi Masuda" . "masutaka.net@gmail.com")) (:maintainers ("Takashi Masuda" . "masutaka.net@gmail.com")) (:maintainer "Takashi Masuda" . "masutaka.net@gmail.com") (:url . "https://github.com/masutaka/emacs-helm-esa"))]) - (helm-etags-plus . [(20201003 1424) ((helm (1 7 8))) "Another Etags helm.el interface" tar ((:commit . "52598fe69636add4b62cd9873041de5c6db9b7ac") (:authors ("纪秀峰(Joseph)" . "jixiuf@gmail.com")) (:maintainers ("纪秀峰(Joseph)" . "jixiuf@gmail.com")) (:maintainer "纪秀峰(Joseph)" . "jixiuf@gmail.com") (:keywords "helm" "etags") (:url . "https://github.com/jixiuf/helm-etags-plus"))]) (helm-evil-markers . [(20200506 715) ((emacs (25 1)) (helm (2 0 0)) (evil (1 2 10))) "Show evil markers with helm" tar ((:commit . "0245f0c268e0eaec85df51ab2deba7ac961f6770") (:authors ("Bill Xue")) (:maintainers ("Bill Xue")) (:maintainer "Bill Xue") (:keywords "extensions") (:url . "https://github.com/xueeinstein/helm-evil-markers"))]) (helm-eww . [(20190315 907) ((emacs (24 4)) (helm (2 8 6)) (seq (1 8))) "Helm UI wrapper for EWW." tar ((:commit . "76ba59fda8dd6f32a1bc7c6df0b43c6f76169911") (:authors ("Pierre Neidhardt" . "mail@ambrevar.xyz")) (:maintainers ("Pierre Neidhardt" . "mail@ambrevar.xyz")) (:maintainer "Pierre Neidhardt" . "mail@ambrevar.xyz") (:keywords "helm" "packages") (:url . "https://github.com/emacs-helm/helm-eww"))]) (helm-ext . [(20200722 107) ((emacs (24 4)) (helm (2 5 3))) "A few extensions to Helm" tar ((:commit . "c30f7772ec577a5ce1de3215f0507826e0725a69") (:authors ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainers ("Junpeng Qiu" . "qjpchmail@gmail.com")) (:maintainer "Junpeng Qiu" . "qjpchmail@gmail.com") (:keywords "extensions"))]) @@ -2393,7 +2390,7 @@ (helm-open-github . [(20220721 415) ((emacs (24 4)) (helm-core (3 6 0)) (gh (0 8 2))) "Utilities of Opening Github Page" tar ((:commit . "5e6d700d1b484bd6cd44bc30674e96d157870c3f") (: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-open-github"))]) (helm-org . [(20231022 620) ((helm (3 3)) (emacs (24 4))) "Helm for org headlines and keywords completion" tar ((:commit . "c80e53315ce6b096e2d0e630702df924bf00bf6a") (:authors ("Thierry Volpiatto" . "thierry.volpiatto@gmail.com")) (:maintainers ("Thierry Volpiatto" . "thierry.volpiatto@gmail.com")) (:maintainer "Thierry Volpiatto" . "thierry.volpiatto@gmail.com") (:url . "https://github.com/emacs-helm/helm-org"))]) (helm-org-multi-wiki . [(20210228 1853) ((emacs (26 1)) (org (9 3)) (org-multi-wiki (0 4)) (org-ql (0 5)) (dash (2 18)) (helm-org-ql (0 5)) (helm (3 5))) "Helm interface to org-multi-wiki" tar ((:commit . "c85bcaafed749de3efa5e1f4d256e7ac9c5678e2") (:authors ("Akira Komamura" . "akira.komamura@gmail.com")) (:maintainers ("Akira Komamura" . "akira.komamura@gmail.com")) (:maintainer "Akira Komamura" . "akira.komamura@gmail.com") (:keywords "org" "outlines") (:url . "https://github.com/akirak/org-multi-wiki"))]) - (helm-org-ql . [(20240221 1146) ((emacs (26 1)) (dash (2 18 1)) (s (1 12 0)) (helm-org (1 0)) (org-ql (0 6 -1))) "Helm support for org-ql" tar ((:commit . "bfff0b5517d55d01bf12de27e10a73c9a077767b") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:url . "https://github.com/alphapapa/org-ql"))]) + (helm-org-ql . [(20240403 2026) ((emacs (26 1)) (dash (2 18 1)) (s (1 12 0)) (helm-org (1 0)) (org-ql (0 6 -1))) "Helm support for org-ql" tar ((:commit . "1f264bf4649dc2ad90f16a763794561ee6164d84") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:url . "https://github.com/alphapapa/org-ql"))]) (helm-org-recent-headings . [(20211011 1519) ((emacs (26 1)) (org (9 0 5)) (dash (2 18 0)) (helm (1 9 4)) (org-recent-headings (0 2 -1)) (s (1 12 0))) "Helm source for org-recent-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"))]) (helm-org-rifle . [(20230821 1927) ((emacs (24 4)) (dash (2 12)) (f (0 18 1)) (helm (1 9 4)) (s (1 10 0))) "Rifle through your Org files" tar ((:commit . "03a52265040b8c6510a8269213d750c451779c38") (: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/helm-org-rifle"))]) (helm-orgcard . [(20220721 756) ((helm-core (3 6 0))) "browse the orgcard by helm" tar ((:commit . "d58d35627bb1714bb2cb095f696706b6881233ed") (:authors ("Yuhei Maeda <yuhei.maeda_at_gmail.com>")) (:maintainers ("Yuhei Maeda")) (:maintainer "Yuhei Maeda") (:keywords "convenience" "helm" "org") (:url . "https://github.com/emacs-jp/helm-orgcard"))]) @@ -2527,7 +2524,6 @@ (holiday-pascha-etc . [(20160822 58) nil "Eastern Christian analog to holiday-easter-etc" tar ((:commit . "eb198656f63cb8679fb0e3a8248782df071a0f3c") (:authors ("Mark A. Hershberger" . "mah@everybody.org")) (:maintainers ("Mark A. Hershberger" . "mah@everybody.org")) (:maintainer "Mark A. Hershberger" . "mah@everybody.org") (:url . "http://github.com/hexmode/holiday-pascha-etc"))]) (holy-books . [(20211025 127) ((s (1 12 0)) (dash (2 16 0)) (emacs (27 1)) (org (9 1))) "Org-mode links/tooltips/lookups for Quran & Bible" tar ((:commit . "02c2956d36631d3d8c8b4bacdcf0a5cdd1f3136d") (:authors ("Musa Al-hassy" . "alhassy@gmail.com")) (:maintainers ("Musa Al-hassy" . "alhassy@gmail.com")) (:maintainer "Musa Al-hassy" . "alhassy@gmail.com") (:keywords "quran" "bible" "links" "tooltips" "convenience" "comm" "hypermedia") (:url . "https://alhassy.github.io/holy-books/"))]) (home-end . [(20190109 541) ((emacs (24 3)) (keypress-multi-event (1 0))) "Smart multi-purpose home / end keys" tar ((:commit . "30676ceec0d4ad84038cd0d65ee45ae810ab185c") (:authors ("Boruch Baum" . "boruch_baum@gmx.com")) (:maintainers ("Boruch Baum" . "boruch_baum@gmx.com")) (:maintainer "Boruch Baum" . "boruch_baum@gmx.com") (:keywords "abbrev" "convenience" "wp" "keyboard") (:url . "https://www.github.com/Boruch_Baum/emacs-home-end"))]) - (homebrew-mode . [(20220907 1656) ((emacs (24 4)) (inf-ruby (2 4 0)) (dash (1 2 0))) "Minor mode for editing Homebrew formulae" tar ((:commit . "e32da1397ce176766e39c286861ef4c40d64bbf5") (:authors ("Alex Dunn" . "dunn.alex@gmail.com")) (:maintainers ("Alex Dunn" . "dunn.alex@gmail.com")) (:maintainer "Alex Dunn" . "dunn.alex@gmail.com") (:keywords "languages" "homebrew" "brew" "ruby") (:url . "https://github.com/dunn/homebrew-mode"))]) (honcho . [(20230224 420) ((emacs (26 1))) "Run and manage long-running services" tar ((:commit . "95846309c6a4ce45f29f215d43847beb510b6aca") (:authors ("Mario Rodas" . "marsam@users.noreply.github.com")) (:maintainers ("Mario Rodas" . "marsam@users.noreply.github.com")) (:maintainer "Mario Rodas" . "marsam@users.noreply.github.com") (:keywords "convenience") (:url . "https://github.com/emacs-pe/honcho.el"))]) (hookify . [(20141216 2209) ((s (1 9 0)) (dash (1 5 0))) "Interactive commands to create temporary hooks" tar ((:commit . "e76127230716f7fab6662410c03c3872d17a172b") (:authors ("Philippe Vaucher" . "philippe.vaucher@gmail.com")) (:maintainers ("Philippe Vaucher" . "philippe.vaucher@gmail.com")) (:maintainer "Philippe Vaucher" . "philippe.vaucher@gmail.com") (:keywords "hook" "convenience") (:url . "https://github.com/Silex/hookify"))]) (horizon-theme . [(20200720 1832) ((emacs (24 3))) "A beautifully warm dual theme" tar ((:commit . "9595549c514a9376c61d5d303405f6a6982e9e46") (:url . "https://github.com/aodhneine/horizon-theme.el"))]) @@ -2564,7 +2560,7 @@ (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 . [(20240331 2145) ((emacs (27 1))) "GNU Hyperbole: The Everyday Hypertextual Information Manager" tar ((:commit . "32f6c69f8759e5d45af08b8688a7a77d7c72a02b") (: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"))]) + (hyperbole . [(20240401 1927) ((emacs (27 1))) "GNU Hyperbole: The Everyday Hypertextual Information Manager" tar ((:commit . "cad7a428186b4c90bc15c7dd463fe0c0277f5e59") (: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 . [(20240226 743) ((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 5 3))) "P2P filesystem" tar ((:commit . "9895acdd1bc14e9a63e3b6ef050510a67ae59e7d") (: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"))]) @@ -2730,7 +2726,6 @@ (ivy-bibtex . [(20210927 1205) ((bibtex-completion (1 0 0)) (ivy (0 13 0)) (cl-lib (0 5))) "A bibliography manager based on Ivy" tar ((:commit . "bb47f355b0da8518aa3fb516019120c14c8747c9") (:authors ("Justin Burkett" . "justin@burkett.cc")) (:maintainers ("Titus von der Malsburg" . "malsburg@posteo.de")) (:maintainer "Titus von der Malsburg" . "malsburg@posteo.de") (:url . "https://github.com/tmalsburg/helm-bibtex"))]) (ivy-clipmenu . [(20220202 2122) ((emacs (26 1)) (f (0 20 0)) (s (1 12 0)) (dash (2 16 0)) (ivy (0 13 0))) "Ivy client for clipmenu" tar ((:commit . "7c200cd4732821187084fad23547ee3f58365062") (:authors ("William Carroll" . "wpcarro@gmail.com")) (:maintainers ("William Carroll" . "wpcarro@gmail.com")) (:maintainer "William Carroll" . "wpcarro@gmail.com") (:url . "https://github.com/wpcarro/ivy-clipmenu.el"))]) (ivy-clojuredocs . [(20201129 2355) ((edn (1 1 2)) (ivy (0 12 0)) (emacs (24 4))) "Search for help in clojuredocs.org" tar ((:commit . "8b6de19b3578c72d2b88f898e2290d94c04350f9") (:authors ("Wanderson Ferreira" . "iagwanderson@gmail.com")) (:maintainers ("Wanderson Ferreira" . "iagwanderson@gmail.com")) (:maintainer "Wanderson Ferreira" . "iagwanderson@gmail.com") (:keywords "matching") (:url . "https://github.com/wandersoncferreira/ivy-clojuredocs"))]) - (ivy-dired-history . [(20210715 48) ((ivy (0 9 0)) (counsel (0 9 0)) (cl-lib (0 5))) "use ivy to open recent directories" tar ((:commit . "dba848929cb063a5536cb442c70be1099e2f5baa") (:authors ("纪秀峰" . "jixiuf@gmail.com")) (:maintainers ("纪秀峰" . "jixiuf@gmail.com")) (:maintainer "纪秀峰" . "jixiuf@gmail.com") (:url . "https://github.com/jixiuf/ivy-dired-history"))]) (ivy-emms . [(20231112 1621) ((ivy (0 13 0)) (emms (0 0)) (emacs (24 4))) "Ivy interface to emms tracks" tar ((:commit . "3b1bda7be64ba5555672b6375c205e0f7d831bc0") (:authors ("Fran Burstall" . "fran.burstall@gmail.com")) (:maintainers ("Fran Burstall" . "fran.burstall@gmail.com")) (:maintainer "Fran Burstall" . "fran.burstall@gmail.com") (:keywords "multimedia") (:url . "https://github.com/franburstall/ivy-emms"))]) (ivy-emoji . [(20200316 2351) ((emacs (26 1)) (ivy (0 13 0))) "Insert emojis with ivy" tar ((:commit . "45894a1f8f8c67b142e1dd1113f47d703dea0b59") (:authors ("Gabriele Bozzola" . "sbozzolator@gmail.com")) (:maintainers ("Gabriele Bozzola" . "sbozzolator@gmail.com")) (:maintainer "Gabriele Bozzola" . "sbozzolator@gmail.com") (:keywords "emoji" "ivy" "convenience") (:url . "https://github.com/sbozzolo/ivy-emoji.git"))]) (ivy-erlang-complete . [(20211019 447) ((async (1 9)) (counsel (0 13 4)) (ivy (0 13 4)) (erlang (19 2)) (emacs (25 1))) "Erlang context sensitive completion at point using ivy. It also support xref and eldoc." tar ((:commit . "6913f6ef7c942a5a2c42bc17635d09c91353e7ca") (:authors ("Sergey Kostyaev" . "feo.me@ya.ru")) (:maintainer "Sergey Kostyaev" . "feo.me@ya.ru") (:keywords "languages" "tools"))]) @@ -2799,7 +2794,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 . [(20240311 821) ((emacs (27 1)) (compat (29 1 4 4))) "Enchanted Spell Checker" tar ((:commit . "b09efcb85b1a8db8054a3d5a298e8d9516836f16") (: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 . [(20240404 444) ((emacs (27 1)) (compat (29 1 4 4))) "Enchanted Spell Checker" tar ((:commit . "a3d69abcb44555ddf5dbf03eee3c6188a456eceb") (: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"))]) @@ -2807,7 +2802,7 @@ (jit-lock-stealth-progress . [(20230808 1342) ((emacs (28 1))) "JIT lock stealth mode-line progress" tar ((:commit . "46d7a8a02a01c81125e35c81fdec5295bb4cbcd2") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainers ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-jit-lock-stealth-progress"))]) (jknav . [(20121006 2025) nil "Automatically enable j/k keys for line-based navigation" tar ((:commit . "861245715c728503dad6573278fdd75c271dbf8b") (:authors ("Aaron Culich" . "aculich@gmail.com")) (:maintainers ("Aaron Culich" . "aculich@gmail.com")) (:maintainer "Aaron Culich" . "aculich@gmail.com") (:keywords "keyboard" "navigation"))]) (jmespath . [(20240115 1310) ((emacs (24 3))) "Query JSON using jmespath" tar ((:commit . "d3a4a4abdd6804d3aef5e0d0c538abd27667b4c3") (:authors ("Shubham Kumar" . "unresolved.shubham@gmail.com")) (:maintainers ("Shubham Kumar" . "unresolved.shubham@gmail.com")) (:maintainer "Shubham Kumar" . "unresolved.shubham@gmail.com") (:keywords "json" "data" "languages" "tools") (:url . "https://github.com/unresolvedcold/jmespath"))]) - (jmt-mode . [(20240331 2056) ((emacs (27 1))) "Java Mode Tamed" tar ((:commit . "a60a5d7894080c81e8cbe83d837e217aec415df3") (:authors ("Michael Allan" . "mike@reluk.ca")) (:maintainers ("Michael Allan" . "mike@reluk.ca")) (:maintainer "Michael Allan" . "mike@reluk.ca") (:keywords "languages" "c") (:url . "http://reluk.ca/project/Java/Emacs/"))]) + (jmt-mode . [(20240401 1900) ((emacs (27 1))) "Java Mode Tamed" tar ((:commit . "9fed14b76d29ba5ef389237c72f2c6f5992730bb") (:authors ("Michael Allan" . "mike@reluk.ca")) (:maintainers ("Michael Allan" . "mike@reluk.ca")) (:maintainer "Michael Allan" . "mike@reluk.ca") (:keywords "languages" "c") (:url . "http://reluk.ca/project/Java/Emacs/"))]) (jonprl-mode . [(20160819 59) ((emacs (24 3)) (cl-lib (0 5)) (yasnippet (0 8 0))) "A major mode for editing JonPRL files" tar ((:commit . "6059bb64891fae45827174e044d6a87ac07172d8") (:authors ("David Raymond Christiansen" . "david@davidchristiansen.dk")) (:maintainers ("David Raymond Christiansen" . "david@davidchristiansen.dk")) (:maintainer "David Raymond Christiansen" . "david@davidchristiansen.dk") (:keywords "languages"))]) (journalctl-mode . [(20240219 2115) ((emacs (27 1))) "Sample major mode for viewing output journalctl" tar ((:commit . "631d10a5c8f466c94c38c3cd7410a27026f5f822") (:authors ("Sebastian Meisel" . "sebastian.meisel@gmail.com")) (:maintainers ("Sebastian Meisel" . "sebastian.meisel@gmail.com")) (:maintainer "Sebastian Meisel" . "sebastian.meisel@gmail.com") (:keywords "unix") (:url . "https://github.com/SebastianMeisel/journalctl-mode"))]) (jpop . [(20170410 1250) ((emacs (24)) (dash (2 11 0)) (cl-lib (0 5))) "Lightweight project caching and navigation framework" tar ((:commit . "7628b03260be96576b34459d45959ee77d8b2110") (:authors ("Dom Charlesworth" . "dgc336@gmail.com")) (:maintainers ("Dom Charlesworth" . "dgc336@gmail.com")) (:maintainer "Dom Charlesworth" . "dgc336@gmail.com") (:keywords "project" "convenience") (:url . "https://github.com/domtronn/jpop.el"))]) @@ -2846,7 +2841,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 . [(20240315 1515) ((emacs (29 1))) "Extends JSX/TSX built-in support" tar ((:commit . "e2fb775b30c2eadd33413d6b1dbdc41ba155c1fe") (: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 . [(20240402 1319) ((emacs (29 1))) "Extends JSX/TSX built-in support" tar ((:commit . "0e5583163f2e5378097ce4690fe69bb61b5ec67e") (: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 . [(20240315 1726) ((emacs (26 1))) "Major mode for editing Julia source code" tar ((:commit . "2dfc869ff6b3878407fe7226669dfaae8d38d541") (:keywords "languages") (:url . "https://github.com/JuliaEditorSupport/julia-emacs"))]) (julia-repl . [(20231026 1005) ((emacs (25 1)) (s (1 12))) "A minor mode for a Julia REPL" tar ((:commit . "4947319bc948b3f80d61b0d65a719737275949b8") (: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"))]) @@ -2889,7 +2884,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 . [(20230326 33) ((emacs (28 1)) (async (1 9 7)) (dash (2 19 1)) (f (0 20 0)) (ht (2 3)) (plz (0 4)) (s (1 13 0)) (yaml (0 5 1))) "Spritzy Kubernetes cluster management" tar ((:commit . "15e841fb7bbc08545534e466ce831d6e80fd8901") (: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 . [(20240402 1638) ((emacs (28 1)) (async (1 9 7)) (dash (2 19 1)) (f (0 20 0)) (ht (2 3)) (plz (0 4)) (s (1 13 0)) (yaml (0 5 1))) "Spritzy Kubernetes cluster management" tar ((:commit . "f67b959ebf993783f6f6011715a42324c6e3bcb3") (: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"))]) @@ -2954,7 +2949,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 . [(20240317 1949) ((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 . "bd88c08c02203a66048412672b894c0d7dab3da3") (: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 . [(20240404 736) ((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 . "ca6a02c567adb562305d0887140d631e2d6f01f6") (: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"))]) @@ -2987,7 +2982,7 @@ (launchctl . [(20210611 2243) ((emacs (24 1))) "Interface to launchctl on Mac OS X." tar ((:commit . "c9b7e93f5ec6fa504dfb03d60571cf3e5dc38e12") (:authors ("Peking Duck <github.com/pekingduck>")) (:maintainers ("Peking Duck <github.com/pekingduck>")) (:maintainer "Peking Duck <github.com/pekingduck>") (:keywords "tools" "convenience") (:url . "http://github.com/pekingduck/launchctl-el"))]) (lavender-theme . [(20170808 1313) ((emacs (24 0))) "an Emacs 24 theme based on Lavender (tmTheme)" tar ((:commit . "ef5e959b95d7fb8152137bc186c4c24e986c1e3c") (:authors ("Jason Milkins")) (:maintainers ("Jason Milkins")) (:maintainer "Jason Milkins") (:url . "https://github.com/emacsfodder/tmtheme-to-deftheme"))]) (lavenderless-theme . [(20201222 1627) ((colorless-themes (0 2))) "A mostly colorless version of lavender-theme" tar ((:commit . "1b2a507b3b7f9559c944af8fc7531a60b38ae0c3") (:authors ("Thomas Letan" . "lthms@soap.coffee")) (:maintainers ("Thomas Letan" . "lthms@soap.coffee")) (:maintainer "Thomas Letan" . "lthms@soap.coffee") (:keywords "faces" "theme") (:url . "https://git.sr.ht/~lthms/colorless-themes.el"))]) - (lazy-ruff . [(20240326 1943) ((emacs (24 3)) (org (9 1))) "Integration with the Ruff Python linter/formatter" tar ((:commit . "90cbeb77a370448364d2eb59870eee781636beff") (:authors ("Christopher Buch Madsen")) (:maintainers ("Christopher Buch Madsen")) (:maintainer "Christopher Buch Madsen") (:keywords "languages" "tools") (:url . "http://github.com/yourusername/emacs-lazy-ruff"))]) + (lazy-ruff . [(20240402 2200) ((emacs (24 3)) (org (9 1))) "Integration with the Ruff Python linter/formatter" tar ((:commit . "c180bb6b0a1d9a65f8963d1b67f09086412bd95f") (:authors ("Christopher Buch Madsen")) (:maintainers ("Christopher Buch Madsen")) (:maintainer "Christopher Buch Madsen") (:keywords "languages" "tools") (:url . "http://github.com/christophermadsen/emacs-lazy-ruff"))]) (lcb-mode . [(20160816 630) ((emacs (24))) "LiveCode Builder major mode" tar ((:commit . "be0768e9aa6f9b8e76f2230f4f7f4d152a766b9a") (:authors ("Peter TB Brett" . "peter@peter-b.co.uk")) (:maintainers ("Peter TB Brett" . "peter@peter-b.co.uk")) (:maintainer "Peter TB Brett" . "peter@peter-b.co.uk") (:keywords "languages") (:url . "https://github.com/peter-b/lcb-mode"))]) (lcr . [(20221012 742) ((dash (2 12 0)) (emacs (25 1))) "lightweight coroutines" tar ((:commit . "6c345112ffb59f3e7babca6c83942f686b5f554b") (:authors ("Jean-Philippe Bernardy" . "jeanphilippe.bernardy@gmail.com")) (:maintainers ("Jean-Philippe Bernardy" . "jeanphilippe.bernardy@gmail.com")) (:maintainer "Jean-Philippe Bernardy" . "jeanphilippe.bernardy@gmail.com") (:keywords "tools") (:url . "https://github.com/jyp/lcr"))]) (le-thesaurus . [(20230112 1604) ((request (0 3 2)) (emacs (24 4))) "Query thesaurus.com for synonyms of a given word" tar ((:commit . "83e8df8957a3b8167cc2bf97849a1eca555ce9a6") (:url . "https://github.com/AnselmC/le-thesaurus.el"))]) @@ -3002,7 +2997,7 @@ (leanote . [(20161223 139) ((emacs (24 4)) (cl-lib (0 5)) (request (0 2)) (let-alist (1 0 3)) (pcache (0 4 0)) (s (1 10 0)) (async (1 9))) "A minor mode writing markdown leanote" tar ((:commit . "d499e7b59bb1f1a2fabc0e4c26fb101ed62ebc7b") (:authors ("Aborn Jiang" . "aborn.jiang@gmail.com")) (:maintainers ("Aborn Jiang" . "aborn.jiang@gmail.com")) (:maintainer "Aborn Jiang" . "aborn.jiang@gmail.com") (:keywords "leanote" "note" "markdown") (:url . "https://github.com/aborn/leanote-emacs"))]) (learn-ocaml . [(20211003 1412) ((emacs (25 1))) "Emacs frontend for learn-ocaml" tar ((:commit . "abdc263537a6a534152a4eaaa17b2c3e4e10418b") (:url . "https://github.com/pfitaxel/learn-ocaml.el"))]) (ledger-import . [(20230904 1837) ((emacs (25 1))) "Fetch OFX files from bank and push them to Ledger" tar ((:commit . "e47e8508794462986b982d6ce3d05bcd17c19242") (:authors ("Damien Cassou" . "damien@cassou.me")) (:maintainers ("Damien Cassou" . "damien@cassou.me")) (:maintainer "Damien Cassou" . "damien@cassou.me") (:url . "https://gitlab.petton.fr/mpdel/libmpdel"))]) - (ledger-mode . [(20240326 1834) ((emacs (25 1))) "Helper code for use with the \"ledger\" command-line tool" tar ((:commit . "e6946feced023c223650ac31cbe5c96bea2a7afb"))]) + (ledger-mode . [(20240326 2002) ((emacs (25 1))) "Helper code for use with the \"ledger\" command-line tool" tar ((:commit . "194aca2f5b9c3ec18bd5037ef10e8f198aac7382"))]) (leerzeichen . [(20220626 835) nil "Minor mode to display whitespace characters." tar ((:commit . "9d4126d5f6563569080845a69b0867119a9fd6ea") (:authors ("Felix Geller" . "fgeller@gmail.com")) (:maintainers ("Felix Geller" . "fgeller@gmail.com")) (:maintainer "Felix Geller" . "fgeller@gmail.com") (:keywords "whitespace" "characters") (:url . "http://github.com/fgeller/leerzeichen.el"))]) (leetcode . [(20230524 1851) ((emacs (26 1)) (dash (2 16 0)) (graphql (0 1 1)) (spinner (1 7 3)) (aio (1 0)) (log4e (0 3 3))) "An leetcode client" tar ((:commit . "e5701191a98d043acdc7112f28d67e3d5c848228") (:authors ("Wang Kai" . "kaiwkx@gmail.com")) (:maintainers ("Wang Kai" . "kaiwkx@gmail.com")) (:maintainer "Wang Kai" . "kaiwkx@gmail.com") (:keywords "extensions" "tools") (:url . "https://github.com/kaiwk/leetcode.el"))]) (legalese . [(20200119 2248) nil "Add legalese to your program files" tar ((:commit . "e465471d2d5a62d35073d93e0f8d40387a82e302") (:authors ("Jorgen Schaefer" . "forcer@forcix.cx")) (:maintainers ("Jorgen Schaefer" . "forcer@forcix.cx")) (:maintainer "Jorgen Schaefer" . "forcer@forcix.cx") (:keywords "convenience") (:url . "https://github.com/jorgenschaefer/legalese"))]) @@ -3067,7 +3062,7 @@ (litanize . [(20230419 917) ((emacs (24 1)) (enlive (0 0 1)) (s (1 12 0))) "Generate \"Latour Litanies\"" tar ((:commit . "a45902fa29c16ef9606229cb01a5441ea754f11b") (:authors ("nik gaffney" . "nik@fo.am")) (:maintainers ("nik gaffney" . "nik@fo.am")) (:maintainer "nik gaffney" . "nik@fo.am") (:keywords "tools" "latour litany" "alien phenomenology" "ontography" "metaphorism" "carpentry") (:url . "https://github.com/zzkt/litanizer"))]) (litecoin-ticker . [(20160612 11) ((json (1 2))) "litecoin price in modeline" tar ((:commit . "3d8047c736e4ee0b8638953f8cc63eaefad34106") (:authors ("Zhe Lei")) (:maintainers ("Zhe Lei")) (:maintainer "Zhe Lei"))]) (literal-string . [(20191023 733) ((emacs (25)) (edit-indirect (0 1 5))) "edit string literals in a dedicated buffer" tar ((:commit . "afffa86e626798ee9f9188ea3be2d5ee6ad17c39") (:authors ("Joost Diepenmaat" . "joost@zeekat.nl")) (:maintainers ("Joost Diepenmaat" . "joost@zeekat.nl")) (:maintainer "Joost Diepenmaat" . "joost@zeekat.nl") (:keywords "lisp" "tools" "docs") (:url . "https://github.com/joodie/literal-string-mode/"))]) - (literate-calc-mode . [(20231125 29) ((emacs (27)) (dash (2 19 1)) (s (1 12 0))) "Inline results from calc" tar ((:commit . "a1a78f26dea70aac1c4df7f5767c8f5c1643d7d1") (:authors ("Robin Schroer")) (:maintainers ("Robin Schroer")) (:maintainer "Robin Schroer") (:keywords "calc" "languages" "tools") (:url . "https://github.com/sulami/literate-calc-mode.el"))]) + (literate-calc-mode . [(20240402 808) ((emacs (27)) (dash (2 19 1)) (s (1 12 0))) "Inline results from calc" tar ((:commit . "ceddadc4f130626d3430fc8c5cfff71628221a60") (:authors ("Robin Schroer")) (:maintainers ("Robin Schroer")) (:maintainer "Robin Schroer") (:keywords "calc" "languages" "tools") (:url . "https://github.com/sulami/literate-calc-mode.el"))]) (literate-coffee-mode . [(20170211 1515) ((coffee-mode (0 5 0))) "major-mode for Literate CoffeeScript" tar ((:commit . "ef34c3a5b813ef078d44c29887761950ab6821c7") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainers ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-literate-coffee-mode"))]) (literate-elisp . [(20220626 932) ((emacs (26 1))) "Load Emacs Lisp code blocks from Org files" tar ((:commit . "bbc4befbf13f63b92cb1d780501482ae5bd8285b") (:authors ("Jingtao Xu" . "jingtaozf@gmail.com")) (:maintainers ("Jingtao Xu" . "jingtaozf@gmail.com")) (:maintainer "Jingtao Xu" . "jingtaozf@gmail.com") (:keywords "lisp" "docs" "extensions" "tools") (:url . "https://github.com/jingtaozf/literate-elisp"))]) (litex-mode . [(20221107 147) ((emacs (24 4)) (units-mode (0 1 1))) "Minor mode for converting lisp to LaTeX" tar ((:commit . "45004b3a865771799b739d17ebb7849190fffa63") (:authors ("Gaurav Atreya" . "allmanpride@gmail.com")) (:maintainers ("Gaurav Atreya" . "allmanpride@gmail.com")) (:maintainer "Gaurav Atreya" . "allmanpride@gmail.com") (:keywords "calculator" "lisp" "latex") (:url . "https://github.com/Atreyagaurav/litex-mode"))]) @@ -3079,7 +3074,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 . [(20240323 2237) nil "Compact syntax for short lambda" tar ((:commit . "c1f4b4ff28fbb4e4bb72473b26b8fd103ccc3ca2") (:keywords "extensions") (:url . "https://git.sr.ht/~tarsius/llama"))]) + (llama . [(20240403 1724) nil "Compact syntax for short lambda" tar ((:commit . "9e5d7664cfdee41e59260ed89c169ecdd782116d") (:keywords "extensions") (:url . "https://git.sr.ht/~tarsius/llama"))]) (llama-cpp . [(20231228 2043) ((emacs (27 1)) (dash (2 19 1))) "A client for llama-cpp server" tar ((:commit . "e876c05eca8ffadc39d3c5013c2581f1e9f8f94f") (: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"))]) @@ -3113,7 +3108,7 @@ (lox-mode . [(20200619 1700) ((emacs (24 3))) "Major mode for the Lox programming language" tar ((:commit . "083a2299e188a516d1e46ef2dd1cbb89db1aec49") (:authors ("Timmy Jose" . "zoltan.jose@gmail.com")) (:maintainers ("Timmy Jose" . "zoltan.jose@gmail.com")) (:maintainer "Timmy Jose" . "zoltan.jose@gmail.com") (:keywords "languages" "lox") (:url . "https://github.com/timmyjose-projects/lox-mode"))]) (lpy . [(20231026 1525) ((emacs (25 1)) (lispy (0 27 0))) "A lispy interface to Python" tar ((:commit . "2c086ec162d4456b99a6095c3c335382a8304734") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainers ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "python" "lisp") (:url . "https://github.com/abo-abo/lpy"))]) (lsp-cfn . [(20240112 921) ((emacs (27 0)) (lsp-mode (8 0 0)) (yaml-mode (0 0 15))) "LSP integration for cfn-lsp-extra" tar ((:commit . "2297533003118ebd9db0116b4d3486a987e98ca9") (:authors ("Laurence Warne")) (:maintainers ("Laurence Warne")) (:maintainer "Laurence Warne") (:url . "https://github.com/LaurenceWarne/lsp-cfn.el"))]) - (lsp-dart . [(20240328 2304) ((emacs (27 1)) (lsp-treemacs (0 3)) (lsp-mode (7 0 1)) (dap-mode (0 6)) (f (0 20 0)) (dash (2 14 1)) (dart-mode (1 0 5)) (jsonrpc (1 0 15)) (ht (2 2))) "Dart support lsp-mode" tar ((:commit . "fc3761b63488639272038f1280e19a16f0eb927c") (:keywords "languages" "extensions") (:url . "https://emacs-lsp.github.io/lsp-dart"))]) + (lsp-dart . [(20240401 1646) ((emacs (27 1)) (lsp-treemacs (0 3)) (lsp-mode (7 0 1)) (dap-mode (0 6)) (f (0 20 0)) (dash (2 14 1)) (dart-mode (1 0 5)) (jsonrpc (1 0 15)) (ht (2 2))) "Dart support lsp-mode" tar ((:commit . "ee476aa9bb891873943bb4f9dc4b729921c2de37") (:keywords "languages" "extensions") (:url . "https://emacs-lsp.github.io/lsp-dart"))]) (lsp-docker . [(20240326 1021) ((emacs (27 1)) (dash (2 14 1)) (lsp-mode (6 2 1)) (f (0 20 0)) (s (1 13 0)) (yaml (0 2 0)) (ht (2 0))) "LSP Docker integration" tar ((:commit . "9247c94c90908c7fba6f98e7a3c5d5216579b38f") (:authors ("Ivan Yonchovski" . "yyoncho@gmail.com")) (:maintainers ("Ivan Yonchovski" . "yyoncho@gmail.com")) (:maintainer "Ivan Yonchovski" . "yyoncho@gmail.com") (:keywords "languages" "langserver") (:url . "https://github.com/emacs-lsp/lsp-docker"))]) (lsp-focus . [(20200906 1917) ((emacs (26 1)) (focus (0 1 1)) (lsp-mode (6 1))) "focus.el support for lsp-mode" tar ((:commit . "d01f0af156e4e78dcb9fa8e080a652cf8f221d30") (:authors ("Vibhav Pant")) (:maintainers ("Vibhav Pant")) (:maintainer "Vibhav Pant") (:keywords "languages" "lsp-mode") (:url . "https://github.com/emacs-lsp/lsp-focus"))]) (lsp-grammarly . [(20240229 115) ((emacs (27 1)) (lsp-mode (6 1)) (grammarly (0 3 0)) (request (0 3 0)) (s (1 12 0)) (ht (2 3))) "LSP Clients for Grammarly" tar ((:commit . "39deb23b282785eaffc6ae17838c92c613a49315") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "lsp" "grammarly" "checker") (:url . "https://github.com/emacs-grammarly/lsp-grammarly"))]) @@ -3127,7 +3122,7 @@ (lsp-latex . [(20240324 1218) ((emacs (27 1)) (lsp-mode (6 0)) (consult (0 35))) "LSP-mode client for LaTeX, on texlab" tar ((:commit . "30e5ee2a387bee7b320564d402b3e587cdae536c") (:authors ("ROCKTAKEY" . "rocktakey@gmail.com")) (:maintainers ("ROCKTAKEY" . "rocktakey@gmail.com")) (:maintainer "ROCKTAKEY" . "rocktakey@gmail.com") (:keywords "languages" "tex") (:url . "https://github.com/ROCKTAKEY/lsp-latex"))]) (lsp-ltex . [(20240318 224) ((emacs (27 1)) (lsp-mode (6 1))) "LSP Clients for LTEX" tar ((:commit . "c4bc1515be815b58d76bed2dbc5e7c400c37d6be") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "lsp" "languagetool" "checker") (:url . "https://github.com/emacs-languagetool/lsp-ltex"))]) (lsp-metals . [(20231017 911) ((emacs (27 1)) (scala-mode (1 1)) (lsp-mode (7 0)) (lsp-treemacs (0 2)) (dap-mode (0 3)) (dash (2 18 0)) (f (0 20 0)) (ht (2 0)) (treemacs (3 1)) (posframe (1 4 1))) "Scala Client settings" tar ((:commit . "da7e54ed65f4e153c94b9c54689908dce142ef37") (:authors ("Ross A. Baker" . "ross@rossabaker.com") ("Evgeny Kurnevsky" . "kurnevsky@gmail.com")) (:maintainers ("Ross A. Baker" . "ross@rossabaker.com")) (:maintainer "Ross A. Baker" . "ross@rossabaker.com") (:keywords "languages" "extensions") (:url . "https://github.com/emacs-lsp/lsp-metals"))]) - (lsp-mode . [(20240401 909) ((emacs (27 1)) (dash (2 18 0)) (f (0 20 0)) (ht (2 3)) (spinner (1 7 3)) (markdown-mode (2 3)) (lv (0)) (eldoc (1 11))) "LSP mode" tar ((:commit . "4e37c36df29cdea633b60fa3c56e714c24ae764f") (:authors ("Vibhav Pant, Fangrui Song, Ivan Yonchovski")) (:maintainers ("Vibhav Pant, Fangrui Song, Ivan Yonchovski")) (:maintainer "Vibhav Pant, Fangrui Song, Ivan Yonchovski") (:keywords "languages") (:url . "https://github.com/emacs-lsp/lsp-mode"))]) + (lsp-mode . [(20240403 1643) ((emacs (27 1)) (dash (2 18 0)) (f (0 20 0)) (ht (2 3)) (spinner (1 7 3)) (markdown-mode (2 3)) (lv (0)) (eldoc (1 11))) "LSP mode" tar ((:commit . "8861252880f6c2f6374a7bca2b945e6447eebd5a") (:authors ("Vibhav Pant, Fangrui Song, Ivan Yonchovski")) (:maintainers ("Vibhav Pant, Fangrui Song, Ivan Yonchovski")) (:maintainer "Vibhav Pant, Fangrui Song, Ivan Yonchovski") (:keywords "languages") (:url . "https://github.com/emacs-lsp/lsp-mode"))]) (lsp-mssql . [(20230510 1124) ((emacs (25 1)) (lsp-mode (6 2)) (dash (2 14 1)) (f (0 20 0)) (ht (2 0)) (lsp-treemacs (0 1))) "MSSQL LSP bindings" tar ((:commit . "a0dba8f86a2ace7e800a9dc8f814767625a509af") (:authors ("Ivan Yonchovski" . "yyoncho@gmail.com")) (:maintainers ("Ivan Yonchovski" . "yyoncho@gmail.com")) (:maintainer "Ivan Yonchovski" . "yyoncho@gmail.com") (:keywords "data" "languages") (:url . "https://github.com/emacs-lsp/lsp-mssql"))]) (lsp-origami . [(20230815 704) ((emacs (27 1)) (origami (1 0)) (lsp-mode (6 1))) "origami.el support for lsp-mode" tar ((:commit . "86aa06517910141c3d5054eea5f7723461fce6a6") (:authors ("Vibhav Pant")) (:maintainers ("Vibhav Pant")) (:maintainer "Vibhav Pant") (:keywords "languages" "lsp-mode") (:url . "https://github.com/emacs-lsp/lsp-origami"))]) (lsp-p4 . [(20190127 1049) ((lsp-mode (3 0))) "P4 support for lsp-mode" tar ((:commit . "084e33a5782f9153502d9b03e63d9cbbe81cdaeb") (:authors ("Dmitri Makarov")) (:maintainers ("Dmitri Makarov")) (:maintainer "Dmitri Makarov") (:keywords "lsp" "p4") (:url . "https://github.com/dmakarov/p4ls"))]) @@ -3219,7 +3214,7 @@ (map-progress . [(20190128 16) ((cl-lib (0 6 1))) "mapping macros that report progress" tar ((:commit . "1fb916159cd054c233ce3c80d9d01adfae640297") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainers ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "convenience") (:url . "https://github.com/tarsius/map-progress"))]) (map-regexp . [(20190128 18) ((cl-lib (0 6 1))) "map over matches of a regular expression" tar ((:commit . "ae2d1c22f786ad987aef3e319925e80160a887a0") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainers ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "convenience") (:url . "https://github.com/tarsius/map-regexp"))]) (marcopolo . [(20160421 1004) ((s (1 9 0)) (dash (2 9 0)) (pkg-info (0 5 0)) (request (0 1 0))) "Emacs client to the Docker HUB/Registry API" tar ((:commit . "85db828f2bb4346a811b3326349b1c6d0aae4601") (:authors ("Nicolas Lamirault" . "nicolas.lamirault@gmail.com")) (:maintainers ("Nicolas Lamirault" . "nicolas.lamirault@gmail.com")) (:maintainer "Nicolas Lamirault" . "nicolas.lamirault@gmail.com") (:keywords "docker") (:url . "https://github.com/nlamirault/marcopolo"))]) - (marginalia . [(20240323 2015) ((emacs (27 1)) (compat (29 1 4 4))) "Enrich existing commands with completion annotations" tar ((:commit . "3275d1f85cb020280979a050054b843f7563aea2") (:authors ("Omar Antolín Camarena <omar@matem.unam.mx>, Daniel Mendler" . "mail@daniel-mendler.de")) (: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 "docs" "help" "matching" "completion") (:url . "https://github.com/minad/marginalia"))]) + (marginalia . [(20240404 451) ((emacs (27 1)) (compat (29 1 4 4))) "Enrich existing commands with completion annotations" tar ((:commit . "58eb5fd6e5cc21b12c5455ae69e7ae93579647bc") (:authors ("Omar Antolín Camarena <omar@matem.unam.mx>, Daniel Mendler" . "mail@daniel-mendler.de")) (: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 "docs" "help" "matching" "completion") (:url . "https://github.com/minad/marginalia"))]) (mark-multiple . [(20121118 1554) nil "Sorta lets you mark several regions at once." tar ((:commit . "f6a53c7c5283d640ae718f4548b0fda78877a375") (:authors ("Magnar Sveen" . "magnars@gmail.com")) (:maintainer "Magnar Sveen" . "magnars@gmail.com") (:keywords "marking" "library"))]) (mark-thing-at . [(20231019 1111) ((emacs (26)) (choice-program (0 14))) "Mark a pattern at the current point" tar ((:commit . "06cc38fb92c0c1badb06f6744f0110742ffdfe6c") (:authors ("Paul Landes")) (:maintainers ("Paul Landes")) (:maintainer "Paul Landes") (:keywords "mark" "point" "lisp") (:url . "https://github.com/plandes/mark-thing-at"))]) (mark-tools . [(20130614 1025) nil "Some simple tools to access the mark-ring in Emacs" tar ((:commit . "a11b61effa90bd0abc876d12573674d36fc17f0c") (: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-mark-tools"))]) @@ -3267,7 +3262,7 @@ (melancholy-theme . [(20230330 835) ((emacs (27 1))) "A dark theme that's pretty sad -*- lexical-binding: t; -" tar ((:commit . "a5c4360f57793401b63b0df382e845b4845c8f97") (:authors ("@baaash" . "bleat@baaa.sh")) (:maintainers ("@baaash" . "bleat@baaa.sh")) (:maintainer "@baaash" . "bleat@baaa.sh") (:keywords "faces" "frames") (:url . "https://gitlab.com/baaash/melancholy-theme"))]) (mellow-theme . [(20170808 1317) ((emacs (24 0))) "an Emacs 24 theme based on Mellow (tmTheme)" tar ((:commit . "2bdf18f05f5212b6f269d9a94afe2cf201766891") (:authors ("Jason Milkins")) (:maintainers ("Jason Milkins")) (:maintainer "Jason Milkins") (:url . "https://github.com/emacsfodder/tmtheme-to-deftheme"))]) (melpa-upstream-visit . [(20130720 1033) ((s (1 6 0))) "A set of kludges to visit a melpa-hosted package's homepage" tar ((:commit . "7310c74fdead3c0f86ad6eff76cf989e63f70f66") (:authors ("Alessandro Piras" . "laynor@gmail.com")) (:maintainers ("Alessandro Piras" . "laynor@gmail.com")) (:maintainer "Alessandro Piras" . "laynor@gmail.com") (:keywords "convenience"))]) - (memento-mori . [(20240330 258) ((emacs (24 3))) "Reminder of our mortality" tar ((:commit . "8d506c18236fa2b7cf1c156be7b028623a3baaf3") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainers ("Ivan Andrus" . "darthandrus@gmail.com")) (:maintainer "Ivan Andrus" . "darthandrus@gmail.com") (:keywords "help") (:url . "https://github.com/gvol/emacs-memento-mori"))]) + (memento-mori . [(20240404 515) ((emacs (24 3))) "Reminder of our mortality" tar ((:commit . "fbcefbc464126197c45891987d238f3ebada914f") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainers ("Ivan Andrus" . "darthandrus@gmail.com")) (:maintainer "Ivan Andrus" . "darthandrus@gmail.com") (:keywords "help") (:url . "https://github.com/gvol/emacs-memento-mori"))]) (memoize . [(20200103 2036) nil "Memoization functions" tar ((:commit . "51b075935ca7070f62fae1d69fe0ff7d8fa56fdd") (:authors ("Christopher Wellons" . "mosquitopsu@gmail.com")) (:maintainers ("Christopher Wellons" . "mosquitopsu@gmail.com")) (:maintainer "Christopher Wellons" . "mosquitopsu@gmail.com") (:url . "https://github.com/skeeto/emacs-memoize"))]) (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"))]) @@ -3280,7 +3275,7 @@ (mermaid-docker-mode . [(20231126 1943) ((emacs (26 1)) (mermaid-mode (1 0))) "Render mermaid graphs with Docker service" tar ((:commit . "9d3421e02704f50e2e695d8dbe6fbb7eb5f2371f") (:authors ("Peter Badida" . "keyweeusr@gmail.com")) (:maintainers ("Peter Badida" . "keyweeusr@gmail.com")) (:maintainer "Peter Badida" . "keyweeusr@gmail.com") (:keywords "convenience" "docker" "mermaid" "mmd" "graph" "design" "jpg" "image" "api") (:url . "https://github.com/KeyWeeUsr/mermaid-docker-mode"))]) (mermaid-mode . [(20240123 1729) ((emacs (25 3))) "major mode for working with mermaid graphs" tar ((:commit . "d8bfb8c819cda9ead19c871842f6b0b8d56c56c0") (:authors ("Adrien Brochard")) (:maintainers ("Adrien Brochard")) (:maintainer "Adrien Brochard") (:keywords "mermaid" "graphs" "tools" "processes") (:url . "https://github.com/abrochard/mermaid-mode"))]) (mermaid-ts-mode . [(20231001 1704) ((emacs (29 1))) "Major mode for Mermaid" tar ((:commit . "3f3a537d249b44e939d6a0d65a6c316761ff8c41") (:authors ("Jonathan Hope" . "jhope@theflatfield.net")) (:maintainers ("Jonathan Hope" . "jhope@theflatfield.net")) (:maintainer "Jonathan Hope" . "jhope@theflatfield.net") (:keywords "mermaid" "languages") (:url . "https://github.com/JonathanHope/mermaid-ts-mode"))]) - (meson-mode . [(20240211 823) ((emacs (26 1))) "Major mode for the Meson build system files" tar ((:commit . "ed7d3071059429d816a878f879cb5cfabfa34485") (:authors ("Michal Sojka" . "sojkam1@fel.cvut.cz")) (:maintainers ("Michal Sojka" . "sojkam1@fel.cvut.cz")) (:maintainer "Michal Sojka" . "sojkam1@fel.cvut.cz") (:keywords "languages" "tools") (:url . "https://github.com/wentasah/meson-mode"))]) + (meson-mode . [(20240218 1834) ((emacs (26 1))) "Major mode for the Meson build system files" tar ((:commit . "c8f4fbf075bb5db2bc0872afe02af2edac075e4e") (:authors ("Michal Sojka" . "sojkam1@fel.cvut.cz")) (:maintainers ("Michal Sojka" . "sojkam1@fel.cvut.cz")) (:maintainer "Michal Sojka" . "sojkam1@fel.cvut.cz") (:keywords "languages" "tools") (:url . "https://github.com/wentasah/meson-mode"))]) (mess . [(20230718 1533) ((emacs (27 1)) (mame (1 0))) "Front-end for MAME MESS" tar ((:commit . "65392b0d0ded45de789d4deab28a4ce88de24567") (:authors ("Yong" . "luo.yong.name@gmail.com")) (:maintainers ("Yong" . "luo.yong.name@gmail.com")) (:maintainer "Yong" . "luo.yong.name@gmail.com") (:url . "https://github.com/Iacob/elmame"))]) (message-attachment-reminder . [(20230124 520) ((emacs (24 1))) "Remind if missing attachment" tar ((:commit . "975381d6e7c6771c462e73abd3398a4ed2a9b86b") (:authors ("Alex Murray" . "murray.alex@gmail.com")) (:maintainers ("Alex Murray" . "murray.alex@gmail.com")) (:maintainer "Alex Murray" . "murray.alex@gmail.com") (:url . "https://github.com/alexmurray/message-attachment-reminder"))]) (message-view-patch . [(20210904 2227) ((emacs (24 4)) (magit (3 0 0))) "Colorize patch-like emails in mu4e" tar ((:commit . "50dd3d92a1794f24b7e375b74e5199c63b54a2d8") (:authors ("Sean Farley")) (:maintainers ("Sean Farley")) (:maintainer "Sean Farley") (:keywords "extensions" "mu4e" "gnus") (:url . "https://github.com/seanfarley/message-view-patch"))]) @@ -3484,7 +3479,7 @@ (ncl-mode . [(20180129 703) ((emacs (24))) "Major Mode for editing NCL scripts and other goodies" tar ((:commit . "602292712a9e6b7e7c25155978999e77d06b7338") (:authors ("Yagnesh Raghava Yakkala" . "hi@yagnesh.org")) (:maintainer "Yagnesh Raghava Yakkala" . "hi@yagnesh.org") (:keywords "ncl" "major mode" "ncl-mode" "atmospheric science.") (:url . "https://github.com/yyr/ncl-mode"))]) (nclip . [(20130617 2015) nil "Network (HTTP) Clipboard" tar ((:commit . "af88e38b1f04be02bf2e57affc662dbd0f828e67") (:authors ("Marian Schubert" . "marian.schubert@gmail.com")) (:maintainers ("Marian Schubert" . "marian.schubert@gmail.com")) (:maintainer "Marian Schubert" . "marian.schubert@gmail.com") (:keywords "nclip" "clipboard" "network") (:url . "http://www.github.com/maio/nclip.el"))]) (neato-graph-bar . [(20181130 1649) ((emacs (24 3))) "Neat-o graph bars CPU/memory etc." tar ((:commit . "a7ae35afd67911e8924f36e646bce0d3e3c1bbe6") (:authors ("Robert Cochran" . "robert-git@cochranmail.com")) (:maintainers ("Robert Cochran" . "robert-git@cochranmail.com")) (:maintainer "Robert Cochran" . "robert-git@cochranmail.com") (:url . "https://gitlab.com/RobertCochran/neato-graph-bar"))]) - (neil . [(20231214 736) ((emacs (27 1))) "companion for Babashka Neil" tar ((:commit . "abfe86474986a36c6c0daa56f8c4b6e1fb883743") (:authors ("Ag Ibragimov" . "agzam.ibragimov@gmail.com")) (:maintainers ("Ag Ibragimov" . "agzam.ibragimov@gmail.com")) (:maintainer "Ag Ibragimov" . "agzam.ibragimov@gmail.com") (:keywords "convenience" "tools") (:url . "https://github.com/babashka/neil"))]) + (neil . [(20240402 1032) ((emacs (27 1))) "companion for Babashka Neil" tar ((:commit . "9a795828e4c201a47c5851157868c06f2ca37448") (:authors ("Ag Ibragimov" . "agzam.ibragimov@gmail.com")) (:maintainers ("Ag Ibragimov" . "agzam.ibragimov@gmail.com")) (:maintainer "Ag Ibragimov" . "agzam.ibragimov@gmail.com") (:keywords "convenience" "tools") (:url . "https://github.com/babashka/neil"))]) (nemerle . [(20161029 2023) nil "major mode for editing nemerle programs" tar ((:commit . "8818c5af5598e16ea59189e1e3245f0a3d7c78f0") (:authors ("Jacek Sliwerski (rzyjontko)" . "rzyj@o2.pl")) (:maintainers ("Jacek Sliwerski (rzyjontko)" . "rzyj@o2.pl")) (:maintainer "Jacek Sliwerski (rzyjontko)" . "rzyj@o2.pl") (:keywords "nemerle" "mode" "languages"))]) (neon-mode . [(20180406 1156) nil "Simple major mode for editing neon files" tar ((:commit . "99d15e46beaf1e7d71e39a00cce810df1f33229d") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainers ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:keywords "conf"))]) (neotree . [(20230815 219) ((cl-lib (0 5))) "A tree plugin like NerdTree for Vim" tar ((:commit . "2b0cb8285352202c62b1e46a8aa265a5e4f9d966") (:authors ("jaypei" . "jaypei97159@gmail.com")) (:maintainers ("jaypei" . "jaypei97159@gmail.com")) (:maintainer "jaypei" . "jaypei97159@gmail.com") (:url . "https://github.com/jaypei/emacs-neotree"))]) @@ -3557,7 +3552,7 @@ (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"))]) - (notmuch . [(20231006 2337) nil "run notmuch within emacs" tar ((:commit . "e4ead7656c72092bf30c43283057c2d4c4107962") (:url . "https://notmuchmail.org"))]) + (notmuch . [(20240404 1120) nil "run notmuch within emacs" tar ((:commit . "8d06dfce175593aebae9a759c9167df4988a3444") (:url . "https://notmuchmail.org"))]) (notmuch-addr . [(20240101 2221) ((emacs (27 1)) (compat (29 1 4 1)) (notmuch (0 37))) "An alternative to notmuch-address.el" tar ((:commit . "1253c6ff1359acef42f04c021c6204b8e726b071") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainers ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (: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"))]) @@ -3681,7 +3676,7 @@ (octicons . [(20151101 340) ((cl-lib (0 5))) "octicons utility" tar ((:commit . "229286a6166dba8ddabc8c4d338798c6cd3cf67d") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainers ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-octicons"))]) (octo-mode . [(20161008 1229) ((emacs (24))) "Major mode for Octo assembly language" tar ((:commit . "4b2ed4a61674f73a6ccd390b5ae123474bd0c977") (:authors ("John Olsson" . "john@cryon.se")) (:maintainers ("John Olsson" . "john@cryon.se")) (:maintainer "John Olsson" . "john@cryon.se") (:keywords "languages") (:url . "https://github.com/cryon/octo-mode"))]) (octopress . [(20190123 107) nil "A lightweight wrapper for Jekyll and Octopress." tar ((:commit . "f2c92d5420f14fc9167c7de1873836510e652de2") (:authors ("Aaron Bieber" . "aaron@aaronbieber.com")) (:maintainers ("Aaron Bieber" . "aaron@aaronbieber.com")) (:maintainer "Aaron Bieber" . "aaron@aaronbieber.com") (:keywords "octopress" "blog") (:url . "https://github.com/aaronbieber/octopress.el"))]) - (oer-reveal . [(20240319 1005) ((emacs (24 4)) (org-re-reveal (3 22 0))) "OER with reveal.js, plugins, and org-re-reveal" tar ((:commit . "0a44a385a565b8e39908c36dd32d5b231ffee9bd") (:authors ("Jens Lechtenbörger")) (:maintainers ("Jens Lechtenbörger")) (:maintainer "Jens Lechtenbörger") (:keywords "hypermedia" "tools" "slideshow" "presentation" "oer") (:url . "https://gitlab.com/oer/oer-reveal"))]) + (oer-reveal . [(20240404 1148) ((emacs (24 4)) (org-re-reveal (3 22 0))) "OER with reveal.js, plugins, and org-re-reveal" tar ((:commit . "fe3b23ff5f5b4d3832ae90ac2908869a94febfb6") (:authors ("Jens Lechtenbörger")) (:maintainers ("Jens Lechtenbörger")) (:maintainer "Jens Lechtenbörger") (:keywords "hypermedia" "tools" "slideshow" "presentation" "oer") (:url . "https://gitlab.com/oer/oer-reveal"))]) (offlineimap . [(20150916 1158) nil "Run OfflineIMAP from Emacs" tar ((:commit . "cc3e067e6237a1eb7b21c575a41683b1febb47f1") (:authors ("Julien Danjou" . "julien@danjou.info")) (:maintainers ("Julien Danjou" . "julien@danjou.info")) (:maintainer "Julien Danjou" . "julien@danjou.info") (:url . "http://julien.danjou.info/offlineimap-el.html"))]) (oj . [(20230212 148) ((emacs (26 1)) (quickrun (2 2))) "Competitive programming tools client for AtCoder, Codeforces" tar ((:commit . "6d586cb108c642bc166c64df113e03193f4d1495") (: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/oj.el"))]) (ol-notmuch . [(20240101 2222) ((emacs (25 1)) (compat (29 1 4 1)) (notmuch (0 37)) (org (9 6 5))) "Links to notmuch messages" tar ((:commit . "881991d94a1ad750633fcf1f2d8a9e0616979be3") (:authors ("Matthieu Lemerre" . "racin@free.fr")) (:maintainers ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "hypermedia" "mail") (:url . "https://git.sr.ht/~tarsius/ol-notmuch"))]) @@ -3755,7 +3750,7 @@ (org-cite-overlay . [(20240207 1611) ((emacs (28 1)) (citeproc (0 9 4))) "Overlays for org-cite citations" tar ((:commit . "cb401787b4569f43815cac55be8a319c489de3de") (:authors ("Samuel W. Flint" . "me@samuelwflint.com")) (:maintainers ("Samuel W. Flint" . "me@samuelwflint.com")) (:maintainer "Samuel W. Flint" . "me@samuelwflint.com") (:keywords "bib" "tex") (:url . "https://git.sr.ht/~swflint/org-cite-overlay"))]) (org-cite-overlay-sidecar . [(20240213 1802) ((emacs (28 1)) (citeproc (0 9 4)) (org-cite-overlay (0 1 0)) (universal-sidecar (1 5 0)) (universal-sidecar-citeproc (1 0 0))) "Show Sidecar for overlaid org-cite citations" tar ((:commit . "bb23142f5d0d390196839fa9b3ce27ce4d149b59") (:authors ("Samuel W. Flint" . "me@samuelwflint.com")) (:maintainers ("Samuel W. Flint" . "me@samuelwflint.com")) (:maintainer "Samuel W. Flint" . "me@samuelwflint.com") (:keywords "bib") (:url . "https://git.sr.ht/~swflint/org-cite-overlay"))]) (org-cliplink . [(20201126 1020) ((emacs (24 4))) "insert org-mode links from the clipboard" tar ((:commit . "13e0940b65d22bec34e2de4bc8cba1412a7abfbc") (:authors ("Alexey Kutepov" . "reximkut@gmail.com")) (:maintainers ("Alexey Kutepov" . "reximkut@gmail.com")) (:maintainer "Alexey Kutepov" . "reximkut@gmail.com") (:url . "http://github.com/rexim/org-cliplink"))]) - (org-clock-agenda-daytime-mode . [(20240303 846) ((org (9 6 18)) (emacs (26 1))) "Display the time clocked today in the modeline" tar ((:commit . "82e2cd5a523f5fda75176a08eb120a0872700add") (:authors ("Arne Babenhauserheide" . "arne_bab@web.de")) (:maintainers ("Arne Babenhauserheide" . "arne_bab@web.de")) (:maintainer "Arne Babenhauserheide" . "arne_bab@web.de") (:keywords "org" "lisp" "clock" "time" "agenda") (:url . "https://www.draketo.de/software/emacs-daytime"))]) + (org-clock-agenda-daytime-mode . [(20240403 1115) ((org (9 6 18)) (emacs (26 1))) "Display the time clocked today in the modeline" tar ((:commit . "f10c7b92a5b2a25f2300b885c2c70526ada50d9c") (:authors ("Arne Babenhauserheide" . "arne_bab@web.de")) (:maintainers ("Arne Babenhauserheide" . "arne_bab@web.de")) (:maintainer "Arne Babenhauserheide" . "arne_bab@web.de") (:keywords "org" "lisp" "clock" "time" "agenda") (:url . "https://www.draketo.de/software/emacs-daytime"))]) (org-clock-convenience . [(20230424 2101) ((org (8)) (emacs (24 3))) "Convenience functions for org time tracking" tar ((:commit . "08417dfd51deb400b890cf71c87b57393fc5ac8c") (: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-clock-convenience"))]) (org-clock-csv . [(20201222 1506) ((org (8 3)) (s (1 0))) "Export `org-mode' clock entries to CSV format." tar ((:commit . "af94b58c2e179a5bcc938f339e93de0eee3da99c") (:authors ("Aaron Jacobs" . "atheriel@gmail.com")) (:maintainers ("Aaron Jacobs" . "atheriel@gmail.com")) (:maintainer "Aaron Jacobs" . "atheriel@gmail.com") (:keywords "calendar" "data" "org") (:url . "https://github.com/atheriel/org-clock-csv"))]) (org-clock-reminder . [(20230222 1956) ((emacs (26 1))) "Notifications that remind you about clocked-in tasks" tar ((:commit . "d3bf97113fd519aa08198e2283ba9c236a6df168") (:authors ("Nikolay Brovko" . "i@nickey.ru")) (:maintainers ("Nikolay Brovko" . "i@nickey.ru")) (:maintainer "Nikolay Brovko" . "i@nickey.ru") (:keywords "calendar" "convenience") (:url . "https://github.com/inickey/org-clock-reminder"))]) @@ -3853,12 +3848,12 @@ (org-projectile . [(20230817 851) ((projectile (2 3 0)) (dash (2 10 0)) (org-project-capture (3 0 1)) (org-category-capture (3 0 1))) "Repository todo capture and management for org-mode with projectile" tar ((:commit . "4ca2667d498fa259772e46ff5e101285446d70b6") (:authors ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainers ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "org-mode" "projectile" "todo" "tools" "outlines" "project" "capture") (:url . "https://github.com/colonelpanic8/org-project-capture"))]) (org-projectile-helm . [(20230817 801) ((org-projectile (1 0 0)) (helm (2 3 1)) (emacs (25))) "helm functions for org-projectile" tar ((:commit . "214a6068c467323a795b27996c1e7b75ae42dc68") (:authors ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainers ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "org" "projectile" "todo" "helm" "outlines") (:url . "https://github.com/IvanMalison/org-projectile"))]) (org-protocol-jekyll . [(20170328 1639) ((cl-lib (0 5))) "Jekyll's handler for org-protocol" tar ((:commit . "dec064a42d6dfe81dfde7ba59ece5ca103ac6334") (:authors ("Vladimir S. Ivanov" . "ivvl82@gmail.com")) (:maintainers ("Vladimir S. Ivanov" . "ivvl82@gmail.com")) (:maintainer "Vladimir S. Ivanov" . "ivvl82@gmail.com"))]) - (org-ql . [(20240222 200) ((emacs (27 1)) (compat (29 1)) (dash (2 18 1)) (f (0 17 2)) (map (2 1)) (org (9 0)) (org-super-agenda (1 2)) (ov (1 0 6)) (peg (1 0 1)) (s (1 12 0)) (transient (0 1)) (ts (0 2 -1))) "Org Query Language, search command, and agenda-like view" tar ((:commit . "e41fe9018a4699532ec875bedddc9746f8e362aa") (: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-ql"))]) + (org-ql . [(20240403 2027) ((emacs (27 1)) (compat (29 1)) (dash (2 18 1)) (f (0 17 2)) (map (2 1)) (org (9 0)) (org-super-agenda (1 2)) (ov (1 0 6)) (peg (1 0 1)) (s (1 12 0)) (transient (0 1)) (ts (0 2 -1))) "Org Query Language, search command, and agenda-like view" tar ((:commit . "c9370982bfd4df04b590762bd795a7da3012c4dd") (: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-ql"))]) (org-radiobutton . [(20210519 1225) ((dash (2 13 0)) (emacs (24))) "Radiobutton for org-mode lists." tar ((:commit . "4ba26bbd26102c45c234bc6ce9a8e9c655c6a0a2") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainers ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:keywords "outlines") (:url . "https://github.com/Fuco1/org-radiobutton"))]) (org-rainbow-tags . [(20230921 2038) ((emacs (28 1))) "Colorize org tags automatically" tar ((:commit . "fd0b68921302fdc3f0d086db7a09b5196251160f") (:authors ("Furkan Karataş" . "furkan.karatas02@gmail.com")) (:maintainers ("Furkan Karataş" . "furkan.karatas02@gmail.com")) (:maintainer "Furkan Karataş" . "furkan.karatas02@gmail.com") (:keywords "faces" "outlines") (:url . "https://github.com/KaratasFurkan/org-rainbow-tags"))]) (org-random-todo . [(20190214 2057) ((emacs (24 3)) (alert (1 3))) "show a random TODO (with alert) every so often" tar ((:commit . "4f7677af740e8f3f7cfaf630ae2e594a125af760") (:authors ("Kevin Brubeck Unhammer" . "unhammer@fsfe.org")) (:maintainers ("Kevin Brubeck Unhammer" . "unhammer@fsfe.org")) (:maintainer "Kevin Brubeck Unhammer" . "unhammer@fsfe.org") (:keywords "org" "todo" "notification" "calendar") (:url . "https://github.com/unhammer/org-random-todo"))]) (org-randomnote . [(20200110 1407) ((f (0 19 0)) (dash (2 12 0)) (org (0))) "Find a random note in your Org-Mode files" tar ((:commit . "ea8cf4385970637efffff8f79e14576ba6d7ad13") (:authors ("Michael Fogleman" . "michaelwfogleman@gmail.com")) (:maintainers ("Michael Fogleman" . "michaelwfogleman@gmail.com")) (:maintainer "Michael Fogleman" . "michaelwfogleman@gmail.com") (:url . "http://github.com/mwfogleman/org-randomnote"))]) - (org-re-reveal . [(20240401 945) ((emacs (24 4)) (org (8 3)) (htmlize (1 34))) "Org export to reveal.js presentations" tar ((:commit . "cabfe07b1fcfcd624ef95dc9aef072851f55784a") (:keywords "tools" "outlines" "hypermedia" "slideshow" "presentation" "oer") (:url . "https://gitlab.com/oer/org-re-reveal"))]) + (org-re-reveal . [(20240404 1135) ((emacs (24 4)) (org (8 3)) (htmlize (1 34))) "Org export to reveal.js presentations" tar ((:commit . "41f06b75d6cc8e050dcb98cc3ea8ff9e5e9fc9f6") (:keywords "tools" "outlines" "hypermedia" "slideshow" "presentation" "oer") (:url . "https://gitlab.com/oer/org-re-reveal"))]) (org-re-reveal-citeproc . [(20211028 1328) ((emacs (25 1)) (org (9 5)) (citeproc (0 9)) (org-re-reveal (3 0 0))) "Citations and bibliography for org-re-reveal" tar ((:commit . "faa9ea387917b20bd1499ad90199ff3d417c00c2") (:authors ("Jens Lechtenbörger")) (:maintainers ("Jens Lechtenbörger")) (:maintainer "Jens Lechtenbörger") (:keywords "hypermedia" "tools" "slideshow" "presentation" "bibliography") (:url . "https://gitlab.com/oer/org-re-reveal-citeproc"))]) (org-re-reveal-ref . [(20211029 551) ((emacs (25 1)) (org-ref (1 1 1)) (org-re-reveal (0 9 3))) "Citations and bibliography for org-re-reveal" tar ((:commit . "ea9661864d5fbef87b12b78f516c13a40c683f24") (:authors ("Jens Lechtenbörger")) (:maintainers ("Jens Lechtenbörger")) (:maintainer "Jens Lechtenbörger") (:keywords "hypermedia" "tools" "slideshow" "presentation" "bibliography") (:url . "https://gitlab.com/oer/org-re-reveal-ref"))]) (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"))]) @@ -4067,7 +4062,7 @@ (paren-completer . [(20160501 1052) ((emacs (24 3))) "Automatically, language agnostically, fill in delimiters." tar ((:commit . "74183a8e13fa1266271bdcbcb4bfb29a4f915f0a") (:authors ("Matthew Bregg")) (:maintainers ("Matthew Bregg")) (:maintainer "Matthew Bregg") (:keywords "convenience") (:url . "https://github.com/MatthewBregg/paren-completer"))]) (paren-face . [(20240101 2224) ((emacs (25 1)) (compat (29 1 4 1))) "A face for parentheses in lisp modes" tar ((:commit . "d36fab3529aa3e046493ec373f2c080874cc184f") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainers ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "faces" "lisp") (:url . "https://github.com/tarsius/paren-face"))]) (parent-mode . [(20240210 1906) nil "get major mode's parent modes" tar ((:commit . "9fe5363b2a190619641c79b3a40d874d8c8f9f40") (:authors ("Fanael Linithien" . "fanael4@gmail.com")) (:maintainers ("Fanael Linithien" . "fanael4@gmail.com")) (:maintainer "Fanael Linithien" . "fanael4@gmail.com") (:url . "https://github.com/Fanael/parent-mode"))]) - (parinfer-rust-mode . [(20240330 1651) ((emacs (26 1))) "An interface for the parinfer-rust library" tar ((:commit . "c47c1cc4053538443b2d4b3960512dd3bf7cc932") (:authors ("Justin Barclay" . "justinbarclay@gmail.com")) (:maintainers ("Justin Barclay" . "justinbarclay@gmail.com")) (:maintainer "Justin Barclay" . "justinbarclay@gmail.com") (:keywords "lisp" "tools") (:url . "https://github.com/justinbarclay/parinfer-rust-mode"))]) + (parinfer-rust-mode . [(20240402 353) ((emacs (26 1))) "An interface for the parinfer-rust library" tar ((:commit . "39f588e3f58ef1a8a301c031f831b8f74fbb3ec1") (:authors ("Justin Barclay" . "justinbarclay@gmail.com")) (:maintainers ("Justin Barclay" . "justinbarclay@gmail.com")) (:maintainer "Justin Barclay" . "justinbarclay@gmail.com") (:keywords "lisp" "tools") (:url . "https://github.com/justinbarclay/parinfer-rust-mode"))]) (parrot . [(20220101 518) ((emacs (24 1))) "Party Parrot rotates gracefully in mode-line." tar ((:commit . "1d381f24d74242018e306d1a0c891bed9a465ac3") (:authors ("Daniel Ting" . "deep.paren.12@gmail.com")) (:maintainers ("Daniel Ting" . "deep.paren.12@gmail.com")) (:maintainer "Daniel Ting" . "deep.paren.12@gmail.com") (:keywords "party" "parrot" "rotate" "sirocco" "kakapo" "games") (:url . "https://github.com/dp12/parrot.git"))]) (parse-csv . [(20160512 1723) nil "Parse strings with CSV fields into s-expressions" tar ((:commit . "96bef1ffbc89ea12d13311c9fa239c5c3e864890") (:authors ("Edward Marco Baringer (Common Lisp)") ("Matt Curtis" . "matt.r.curtis@gmail.com")) (:maintainers ("Matt Curtis" . "matt.r.curtis@gmail.com")) (:maintainer "Matt Curtis" . "matt.r.curtis@gmail.com") (:keywords "csv") (:url . "https://github.com/mrc/el-csv"))]) (parse-it . [(20240101 946) ((emacs (25 1)) (s (1 12 0))) "Basic Parser in Emacs Lisp" tar ((:commit . "cdc4386ef8e94ccdeff3700021d4a944034ae559") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "parse" "parser" "lex" "lexer" "ast") (:url . "https://github.com/jcs-elpa/parse-it"))]) @@ -4214,7 +4209,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 . [(20190720 1957) ((emacs (25 1)) (request (0 2)) (dash (2 13 0)) (kv (0 0 19)) (s (1 12 0))) "Library for accessing getpocket.com API" tar ((:commit . "f794e3e619e1f6cad25bbfd5fe019a7e62820bf4") (: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 . [(20240403 2059) ((emacs (25 1)) (request (0 2)) (dash (2 13 0)) (kv (0 0 19)) (s (1 12 0))) "Library for accessing getpocket.com API" tar ((:commit . "1e53ce8f205eec514d625c2212ab910ab0758b14") (: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 . [(20230904 539) ((emacs (25 1)) (dash (2 13 0)) (kv (0 0 19)) (peg (1 0 1)) (pocket-lib (0 1)) (s (1 10)) (ov (1 0 6)) (rainbow-identifiers (0 2 2)) (org-web-tools (0 1)) (ht (2 2))) "Client for Pocket reading list" tar ((:commit . "ef6b6892ef13eff3479d79c7f6bc918dd0444e88") (: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"))]) @@ -4465,7 +4460,7 @@ (rcirc-groups . [(20170731 2101) nil "an emacs buffer in rcirc-groups major mode" tar ((:commit . "b68ece9d219b909244d4e3c0d8bf6a746d6fead7") (:authors ("Dimitri Fontaine" . "dim@tapoueh.org")) (:maintainers ("Dimitri Fontaine" . "dim@tapoueh.org")) (:maintainer "Dimitri Fontaine" . "dim@tapoueh.org") (:keywords "comm" "convenience") (:url . "http://tapoueh.org/emacs/rcirc-groups.html"))]) (rcirc-notify . [(20150219 2204) nil "libnotify popups" tar ((:commit . "841a7b5a6cdb0c11a812df924d2c6a7d364fd455") (:authors ("Will Farrington, Alex Schroeder <alex@gnu.org>, Nic Ferrier" . "nferrier@ferrier.me.uk")) (:maintainers ("Nic Ferrier" . "nferrier@ferrier.me.uk")) (:maintainer "Nic Ferrier" . "nferrier@ferrier.me.uk") (:keywords "lisp" "rcirc" "irc" "notify" "growl"))]) (rcirc-styles . [(20210414 1712) ((cl-lib (0 5))) "support mIRC-style color and attribute codes" tar ((:commit . "dd06ec5fa455131788bbc885fcfaaec16b08f13b"))]) - (rdf-prefix . [(20230321 1949) nil "Prefix lookup for RDF" tar ((:commit . "70d1ef453fc55f0bfbb4bd3127112332e5b0f623") (:authors ("Simen Heggestøyl" . "simenheg@runbox.com")) (:maintainers ("Simen Heggestøyl" . "simenheg@runbox.com")) (:maintainer "Simen Heggestøyl" . "simenheg@runbox.com") (:keywords "convenience" "abbrev") (:url . "https://github.com/simenheg/rdf-prefix"))]) + (rdf-prefix . [(20240403 1710) nil "Prefix lookup for RDF" tar ((:commit . "c591608d12278b293a14c27ab2df72a269eb535d") (:authors ("Simen Heggestøyl" . "simenheg@runbox.com")) (:maintainers ("Simen Heggestøyl" . "simenheg@runbox.com")) (:maintainer "Simen Heggestøyl" . "simenheg@runbox.com") (:keywords "convenience" "abbrev") (:url . "https://github.com/simenheg/rdf-prefix"))]) (rdxmk . [(20170630 134) nil "A small set of tools for redox developments" tar ((:commit . "e78749fb29738365ffa4d863ffabeb969ebb0bcf") (:authors ("Jacob Salzberg" . "jsalzbergedu@yahoo.com")) (:maintainers ("Jacob Salzberg" . "jsalzbergedu@yahoo.com")) (:maintainer "Jacob Salzberg" . "jsalzbergedu@yahoo.com") (:keywords "redox" "convenience" "tools") (:url . "https://github.com/jsalzbergedu/rdxmk"))]) (react-snippets . [(20210430 1510) ((yasnippet (0 7 0))) "Yasnippets for React" tar ((:commit . "9d0a1bb90ac36c689cded48b661e81d4544fd719") (:authors ("John Mastro" . "john.b.mastro@gmail.com")) (:maintainer "John Mastro" . "john.b.mastro@gmail.com") (:keywords "snippets"))]) (read-aloud . [(20160923 500) ((emacs (24 4))) "A simple interface to TTS engines" tar ((:commit . "d5f80ab72054a957aed25224639c1779cae5f4d1") (:authors ("Alexander Gromnitsky" . "alexander.gromnitsky@gmail.com")) (:maintainers ("Alexander Gromnitsky" . "alexander.gromnitsky@gmail.com")) (:maintainer "Alexander Gromnitsky" . "alexander.gromnitsky@gmail.com") (:keywords "multimedia") (:url . "https://github.com/gromnitsky/read-aloud.el"))]) @@ -4650,7 +4645,7 @@ (saveplace-pdf-view . [(20240209 505) ((emacs (24 1))) "Save place in pdf-view buffers" tar ((:commit . "ee95460cd934080338f03a16f95b549577425216") (:authors ("Nicolai Singh <nicolaisingh at pm.me>")) (:maintainers ("Nicolai Singh <nicolaisingh at pm.me>")) (:maintainer "Nicolai Singh <nicolaisingh at pm.me>") (:keywords "files" "convenience") (:url . "https://github.com/nicolaisingh/saveplace-pdf-view"))]) (say-what-im-doing . [(20160706 1931) nil "dictate what you're doing with text to speech" tar ((:commit . "5b2ce6783b02805bcac1107a149bfba3852cd9d5") (:authors ("Benaiah Mischenko")) (:maintainers ("Benaiah Mischenko")) (:maintainer "Benaiah Mischenko") (:keywords "text to speech" "dumb" "funny") (:url . "http://github.com/benaiah/say-what-im-doing"))]) (sayid . [(20220101 1357) ((cider (0 21 0))) "sayid nREPL middleware client" tar ((:commit . "879aff586336a0ec4d46c0ed4720fb1de22082bd") (:authors ("Bill Piel" . "bill@billpiel.com")) (:maintainers ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "clojure" "cider" "debugger") (:url . "https://github.com/clojure-emacs/sayid"))]) - (sbt-mode . [(20240208 626) ((emacs (24 4))) "Interactive support for sbt projects" tar ((:commit . "bcf8d6040021013430b39d6f6766ce1aab0b691a") (:keywords "languages") (:url . "https://github.com/hvesalai/emacs-sbt-mode"))]) + (sbt-mode . [(20240404 1105) ((emacs (24 4))) "Interactive support for sbt projects" tar ((:commit . "cc68728a6ef0600aad369157b3a2d0ce56afba9b") (:keywords "languages") (:url . "https://github.com/hvesalai/emacs-sbt-mode"))]) (scad-mode . [(20240224 1251) ((emacs (27 1)) (compat (29 1 4 4))) "A major mode for editing OpenSCAD code" tar ((:commit . "1bae3dab16adf1f0d47befcc6d2b63a50af87231") (:authors ("Len Trigg, Łukasz Stelmach, zk_phi, Daniel Mendler")) (:maintainers ("Len Trigg <lenbok@gmail.com>, Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainer "Len Trigg <lenbok@gmail.com>, Daniel Mendler" . "mail@daniel-mendler.de") (:keywords "languages") (:url . "https://github.com/openscad/emacs-scad-mode"))]) (scad-preview . [(20211212 1128) ((scad-mode (91 0)) (emacs (24 4))) "Preview SCAD models in real-time within Emacs" tar ((:commit . "c5449b26c63f3e0a695905a7e4e84f8d844f761b") (:authors ("zk_phi")) (:maintainers ("zk_phi")) (:maintainer "zk_phi") (:url . "https://zk-phi.github.io/"))]) (scala-mode . [(20240113 1743) nil "Major mode for editing Scala" tar ((:commit . "4c6d636b86e3bb1d95de819dc48dda92abdfbcf4") (:keywords "languages") (:url . "https://github.com/hvesalai/emacs-scala-mode"))]) @@ -4724,7 +4719,7 @@ (sexp-move . [(20150915 1730) nil "Improved S-Expression Movement" tar ((:commit . "117f7a91ab7c25e438413753e916570122011ce7") (:authors ("Philip Woods" . "elzairthesorcerer@gmail.com")) (:maintainers ("Philip Woods" . "elzairthesorcerer@gmail.com")) (:maintainer "Philip Woods" . "elzairthesorcerer@gmail.com") (:keywords "sexp") (:url . "https://gitlab.com/elzair/sexp-move"))]) (sexy-monochrome-theme . [(20200115 2146) nil "A sexy dark Emacs >= 24 theme for your sexy code" tar ((:commit . "f3ad07d60c966ef34cb11026eaba053e114bb8f1") (:authors ("Volodymyr Yevtushenko" . "voloyev@vivaldi.net")) (:maintainers ("Volodymyr Yevtushenko" . "voloyev@vivaldi.net")) (:maintainer "Volodymyr Yevtushenko" . "voloyev@vivaldi.net") (:keywords "themes") (:url . "https://github.com/voloyev/sexy-monochrome-theme"))]) (sfz-mode . [(20200716 1023) ((emacs (25 1))) "Major mode for SFZ files" tar ((:commit . "aaf31d1b68817251affed7da719dfcb2acd4b51a") (:authors ("Jean Pierre Cimalando" . "jp-dev@inbox.ru")) (:maintainers ("Jean Pierre Cimalando" . "jp-dev@inbox.ru")) (:maintainer "Jean Pierre Cimalando" . "jp-dev@inbox.ru") (:keywords "languages") (:url . "https://github.com/sfztools/emacs-sfz-mode"))]) - (shackle . [(20211118 1129) ((emacs (24 3)) (cl-lib (0 5))) "Enforce rules for popups" tar ((:commit . "f1467db75a8fa5d51c676181fb308ccbf7b05e6f") (:authors ("Vasilij Schneidermann" . "mail@vasilij.de")) (:maintainers ("Vasilij Schneidermann" . "mail@vasilij.de")) (:maintainer "Vasilij Schneidermann" . "mail@vasilij.de") (:keywords "convenience") (:url . "https://depp.brause.cc/shackle"))]) + (shackle . [(20240402 1315) ((emacs (24 3)) (cl-lib (0 5))) "Enforce rules for popups" tar ((:commit . "ae25e7e0e593520c8590440fe5e3c0ea8053dc26") (:authors ("Vasilij Schneidermann" . "mail@vasilij.de")) (:maintainers ("Vasilij Schneidermann" . "mail@vasilij.de")) (:maintainer "Vasilij Schneidermann" . "mail@vasilij.de") (:keywords "convenience") (:url . "https://depp.brause.cc/shackle"))]) (shadchen . [(20141102 1839) nil "pattern matching for elisp" tar ((:commit . "35f2b9c304eec990c16efbd557198289dc7cbb1f") (:authors ("Vincent Toups")) (:maintainers ("Vincent Toups")) (:maintainer "Vincent Toups"))]) (shader-mode . [(20220930 1052) ((emacs (24))) "Major mode for shader" tar ((:commit . "fe5a1982ba69e4a98b834141a46a1908f132df15") (:authors ("midnightSuyama" . "midnightSuyama@gmail.com")) (:maintainers ("midnightSuyama" . "midnightSuyama@gmail.com")) (:maintainer "midnightSuyama" . "midnightSuyama@gmail.com") (:url . "https://github.com/midnightSuyama/shader-mode"))]) (shades-of-purple-theme . [(20230421 2059) nil "A theme with bold shades of purple" tar ((:commit . "8757594c5f6265b09d156cf9f8671f78863b25db") (:authors ("Arturo Vergara" . "hello@dead.computer")) (:maintainers ("Arturo Vergara" . "hello@dead.computer")) (:maintainer "Arturo Vergara" . "hello@dead.computer") (:url . "https://github.com/arturovm/shades-of-purple-emacs"))]) @@ -4772,11 +4767,11 @@ (side-hustle . [(20240325 205) ((emacs (24 4)) (seq (2 20))) "Hustle through Imenu in a side window" tar ((:commit . "903380cf9e08d98689c2c116965f8e47d002fad6") (:authors ("Paul W. Rankin" . "hello@paulwrankin.com")) (:maintainers ("Paul W. Rankin" . "hello@paulwrankin.com")) (:maintainer "Paul W. Rankin" . "hello@paulwrankin.com") (:keywords "convenience") (:url . "https://github.com/rnkn/side-hustle"))]) (side-notes . [(20230814 302) ((emacs (24 4))) "Easy access to a directory notes file" tar ((:commit . "fbe409066df83a7e64a6a9ddf6d99ce7177fcdbb") (:authors ("Paul W. Rankin" . "hello@paulwrankin.com")) (:maintainers ("Paul W. Rankin" . "hello@paulwrankin.com")) (:maintainer "Paul W. Rankin" . "hello@paulwrankin.com") (:keywords "convenience") (:url . "https://github.com/rnkn/side-notes"))]) (sidecar-locals . [(20240227 151) ((emacs (27 1))) "A flexible alternative to built-in dir-locals" tar ((:commit . "2b2b765387f2cbae9935c3ee6e2a32aa8d68f1b8") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainers ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:keywords "convenience") (:url . "https://codeberg.org/ideasman42/emacs-sidecar-locals"))]) - (sideline . [(20240319 315) ((emacs (27 1)) (ht (2 4))) "Show information on the side" tar ((:commit . "04a525f624e1a42c47a2755fbc63be32104f44cb") (: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-sideline/sideline"))]) + (sideline . [(20240404 900) ((emacs (27 1)) (ht (2 4))) "Show information on the side" tar ((:commit . "c1729b2b9d2ca6b37bf605ca2271e570f30316f0") (: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-sideline/sideline"))]) (sideline-blame . [(20240101 918) ((emacs (27 1)) (sideline (0 1 0)) (vc-msg (1 1 1))) "Show blame messages with sideline" tar ((:commit . "b597c047d2a8ef7dd155e85e43fd65530ecf0a61") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "blame") (:url . "https://github.com/emacs-sideline/sideline-blame"))]) - (sideline-flycheck . [(20240313 2304) ((emacs (27 1)) (sideline (0 1 1)) (flycheck (0 14)) (ht (2 4))) "Show flycheck errors with sideline" tar ((:commit . "1b3fc4d41b93ab3fa3754d188545d9e8cc0150f1") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "flycheck") (:url . "https://github.com/emacs-sideline/sideline-flycheck"))]) + (sideline-flycheck . [(20240402 2255) ((emacs (27 1)) (sideline (0 1 1)) (flycheck (0 14)) (ht (2 4))) "Show flycheck errors with sideline" tar ((:commit . "212b8193becc012d39ccb1e68abf0e19f68764f4") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "flycheck") (:url . "https://github.com/emacs-sideline/sideline-flycheck"))]) (sideline-flymake . [(20240309 1112) ((emacs (27 1)) (sideline (0 1 0))) "Show flymake errors with sideline" tar ((:commit . "e6a9ae69fc048f8eaacbf653e7136678dbef4597") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "flymake") (:url . "https://github.com/emacs-sideline/sideline-flymake"))]) - (sideline-lsp . [(20240101 918) ((emacs (27 1)) (sideline (0 1 0)) (lsp-mode (6 0)) (dash (2 18 0)) (ht (2 4)) (s (1 12 0))) "Show lsp information with sideline" tar ((:commit . "0a085a29b943eede42824981b5d0cd9fb951e9d8") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "lsp") (:url . "https://github.com/emacs-sideline/sideline-lsp"))]) + (sideline-lsp . [(20240403 2210) ((emacs (27 1)) (sideline (0 1 0)) (lsp-mode (6 0)) (dash (2 18 0)) (ht (2 4)) (s (1 12 0))) "Show lsp information with sideline" tar ((:commit . "69aca6403509abb4f5c5ba8499e98f80f81ebc88") (:authors ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Shen, Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Shen, Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "lsp") (:url . "https://github.com/emacs-sideline/sideline-lsp"))]) (sift . [(20200421 1423) nil "Front-end for sift, a fast and powerful grep alternative" tar ((:commit . "cdddba2d183146c340915003f1b5d09d13712c22") (:authors ("Nicolas Lamirault" . "nicolas.lamirault@gmail.com")) (:maintainers ("Nicolas Lamirault" . "nicolas.lamirault@gmail.com")) (:maintainer "Nicolas Lamirault" . "nicolas.lamirault@gmail.com") (:keywords "sift" "ack" "pt" "ag" "grep" "search") (:url . "https://github.com/nlamirault/sift.el"))]) (signal . [(20160816 1438) ((emacs (24)) (cl-lib (0 5))) "Advanced hook" tar ((:commit . "aa58327e2297df921d72a0370468b48663efd438") (:authors ("Mola-T" . "Mola@molamola.xyz")) (:maintainers ("Mola-T" . "Mola@molamola.xyz")) (:maintainer "Mola-T" . "Mola@molamola.xyz") (:keywords "internal" "lisp" "processes" "tools") (:url . "https://github.com/mola-T/signal"))]) (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"))]) @@ -4852,7 +4847,7 @@ (smart-tab . [(20210530 1743) ((emacs (24 3))) "Intelligent tab completion and indentation" tar ((:commit . "2f1b4073904805c8454ebc9bc967b23836a2d577") (:authors ("John SJ Anderson" . "john@genehack.org") ("Sebastien Rocca Serra" . "sroccaserra@gmail.com") ("Daniel Hackney" . "dan@haxney.org")) (:maintainers ("John SJ Anderson" . "john@genehack.org")) (:maintainer "John SJ Anderson" . "john@genehack.org") (:keywords "extensions") (:url . "https://git.genehack.net/genehack/smart-tab"))]) (smart-tabs-mode . [(20200907 2025) nil "Intelligently indent with tabs, align with spaces!" tar ((:commit . "1044c17e42479de943e69cdeb85e4d05ad9cca8c") (:authors ("John Croisant" . "jacius@gmail.com") ("Alan Pearce" . "alan@alanpearce.co.uk") ("Daniel Dehennin" . "daniel.dehennin@baby-gnu.org") ("Matt Renaud" . "mrenaud92@gmail.com")) (:maintainers ("Joel C. Salomon" . "joelcsalomon@gmail.com")) (:maintainer "Joel C. Salomon" . "joelcsalomon@gmail.com") (:keywords "languages") (:url . "http://www.emacswiki.org/emacs/SmartTabs"))]) (smart-window . [(20160717 130) ((cl-lib (0 5))) "vim-like window controlling plugin" tar ((:commit . "5996461b7cbc5ab4509ac48537916eb29a8e4c16") (:authors ("Felix Chern" . "idryman@gmail.com")) (:maintainers ("Felix Chern" . "idryman@gmail.com")) (:maintainer "Felix Chern" . "idryman@gmail.com") (:keywords "window") (:url . "https://github.com/dryman/smart-window.el"))]) - (smartparens . [(20240330 1851) ((dash (2 13 0)) (cl-lib (0 3))) "Automatic insertion, wrapping and paredit-like navigation with user defined pairs." tar ((:commit . "8459f2f7f025baa8c0c98d18531a5fa32f63d949") (:authors ("Matus Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matus Goljer" . "matus.goljer@gmail.com") (:keywords "abbrev" "convenience" "editing") (:url . "https://github.com/Fuco1/smartparens"))]) + (smartparens . [(20240403 2143) ((dash (2 13 0)) (cl-lib (0 3))) "Automatic insertion, wrapping and paredit-like navigation with user defined pairs." tar ((:commit . "3ed34cb67e3cabfb90e4271da3b581887d8b2d78") (:authors ("Matus Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matus Goljer" . "matus.goljer@gmail.com") (:keywords "abbrev" "convenience" "editing") (:url . "https://github.com/Fuco1/smartparens"))]) (smartrep . [(20150509 230) nil "Support sequential operation which omitted prefix keys." tar ((:commit . "f0ff5a6d7b8603603598ae3045c98b011e58d86e") (:authors ("myuhe <yuhei.maeda_at_gmail.com>")) (:maintainers ("myuhe")) (:maintainer "myuhe") (:keywords "convenience") (:url . "https://github.com/myuhe/smartrep.el"))]) (smartscan . [(20170211 2033) nil "Jumps between other symbols found at point" tar ((:commit . "234e077145710a174c20742de792b97ed2f965f6") (:authors ("Mickey Petersen" . "mickey@masteringemacs.org")) (:maintainers ("Mickey Petersen" . "mickey@masteringemacs.org")) (:maintainer "Mickey Petersen" . "mickey@masteringemacs.org") (:keywords "extensions"))]) (smarty-mode . [(20100703 1158) nil "major mode for editing smarty templates" tar ((:commit . "3dfdfe1571f5e9ef55a29c51e5a80046d4cb7568") (:maintainers ("Benj Carson")) (:maintainer "Benj Carson") (:keywords "smarty" "php" "languages" "templates") (:url . "none yet"))]) @@ -5116,7 +5111,7 @@ (teletext . [(20231215 1524) ((emacs (24 3))) "Teletext broadcast viewer" tar ((:commit . "d59ae5f9b79007646815a38f31882a114ca8aee0") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainers ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "comm" "help" "hypermedia") (:url . "https://github.com/lassik/emacs-teletext"))]) (teletext-yle . [(20231215 1609) ((emacs (24 3)) (teletext (0 1))) "Teletext provider for Finnish national network YLE" tar ((:commit . "59a287c26571db07e191ac86cdf0be312fec1964") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainers ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "comm" "help" "hypermedia") (:url . "https://github.com/lassik/emacs-teletext-yle"))]) (tempel . [(20240216 1543) ((emacs (27 1)) (compat (29 1 4 4))) "Tempo templates/snippets with in-buffer field editing" tar ((:commit . "bcc3185202edce67c7f7fc74287cc2ecbeef10c6") (:authors ("Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainers ("Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainer "Daniel Mendler" . "mail@daniel-mendler.de") (:keywords "abbrev" "languages" "tools" "text") (:url . "https://github.com/minad/tempel"))]) - (tempel-collection . [(20240328 1313) ((tempel (0 5)) (emacs (29 1))) "Collection of templates for Tempel" tar ((:commit . "c39bc3f0caa410ac3c6017f9a6d7a10a536ddb26") (:authors ("Vitalii Drevenchuk" . "cradlemann@gmail.com") ("Max Penet" . "mpenetr@s-exp.com") ("Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainers ("Vitalii Drevenchuk" . "cradlemann@gmail.com")) (:maintainer "Vitalii Drevenchuk" . "cradlemann@gmail.com") (:keywords "tools") (:url . "https://github.com/Crandel/tempel-collection"))]) + (tempel-collection . [(20240404 1026) ((tempel (0 5)) (emacs (29 1))) "Collection of templates for Tempel" tar ((:commit . "e87b1fee9554f9bc7415df053c8d8c9f03c13955") (:authors ("Vitalii Drevenchuk" . "cradlemann@gmail.com") ("Max Penet" . "mpenetr@s-exp.com") ("Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainers ("Vitalii Drevenchuk" . "cradlemann@gmail.com")) (:maintainer "Vitalii Drevenchuk" . "cradlemann@gmail.com") (:keywords "tools") (:url . "https://github.com/Crandel/tempel-collection"))]) (templ-ts-mode . [(20240118 338) ((emacs (29 1))) "Major mode for editing Templ files" tar ((:commit . "e43dc22adada160906bd411b03cfa022d787486d") (:authors ("David Anderson" . "dave@natulte.net")) (:maintainers ("David Anderson" . "dave@natulte.net")) (:maintainer "David Anderson" . "dave@natulte.net") (:keywords "languages") (:url . "https://github.com/danderson/templ-ts-mode"))]) (template-overlays . [(20180706 1132) ((emacs (24 4)) (ov (1 0 6))) "Display template regions using overlays" tar ((:commit . "3cbc9a4882dcbbddf9b168883d119a6af0848784") (:authors ("Mariano Montone" . "marianomontone@gmail.com")) (:maintainers ("Mariano Montone" . "marianomontone@gmail.com")) (:maintainer "Mariano Montone" . "marianomontone@gmail.com") (:keywords "faces" "convenience" "templates" "overlays") (:url . "http://www.github.com/mmontone/template-overlays"))]) (templatel . [(20210902 228) ((emacs (25 1))) "Templating language;" tar ((:commit . "e1ccb88cdc4b482b078276960f810b82ba3b7847") (:authors ("Lincoln Clarete" . "lincoln@clarete.li")) (:maintainers ("Lincoln Clarete" . "lincoln@clarete.li")) (:maintainer "Lincoln Clarete" . "lincoln@clarete.li") (:url . "https://clarete.li/templatel"))]) @@ -5138,7 +5133,7 @@ (tern . [(20191227 950) ((json (1 2)) (cl-lib (0 5)) (emacs (24))) "Tern-powered JavaScript integration" tar ((:commit . "0d19800db70a6348c627a69f444b91d21ad89629") (:authors ("Marijn Haverbeke")) (:maintainers ("Marijn Haverbeke")) (:maintainer "Marijn Haverbeke") (:url . "http://ternjs.net/"))]) (tern-auto-complete . [(20191227 950) ((tern (0 0 1)) (auto-complete (1 4)) (cl-lib (0 5)) (emacs (24))) "Tern Completion by auto-complete.el" tar ((:commit . "0d19800db70a6348c627a69f444b91d21ad89629") (:authors ("<m.sakurai at kiwanami.net>")) (:maintainers ("<m.sakurai at kiwanami.net>")) (:maintainer "<m.sakurai at kiwanami.net>"))]) (tern-context-coloring . [(20170102 2253) ((emacs (24 3)) (context-coloring (8 1 0)) (tern (0 0 1))) "Use Tern for context coloring" tar ((:commit . "3a8e979d6cc83aabcb3dda3f5f31a6422532efba") (:authors ("Jackson Ray Hamilton" . "jackson@jacksonrayhamilton.com")) (:maintainers ("Jackson Ray Hamilton" . "jackson@jacksonrayhamilton.com")) (:maintainer "Jackson Ray Hamilton" . "jackson@jacksonrayhamilton.com") (:keywords "convenience" "faces" "tools") (:url . "https://github.com/jacksonrayhamilton/tern-context-coloring"))]) - (terraform-doc . [(20240119 1413) ((emacs (24 4))) "Look up terraform documentation on the fly" tar ((:commit . "1e6963662d50196efb78f906a4d3d8669454dbbb") (:authors ("Giap Tran" . "txgvnn@gmail.com")) (:maintainers ("Giap Tran" . "txgvnn@gmail.com")) (:maintainer "Giap Tran" . "txgvnn@gmail.com") (:keywords "comm") (:url . "https://github.com/TxGVNN/terraform-doc"))]) + (terraform-doc . [(20240328 802) ((emacs (25 1)) (request (0 3 0)) (promise (1 1)) (org (9 2))) "Look up terraform documentation on the fly" tar ((:commit . "32e65a46d1c31d1524002cc50b33082a84045173") (:authors ("Giap Tran" . "txgvnn@gmail.com")) (:maintainers ("Giap Tran" . "txgvnn@gmail.com")) (:maintainer "Giap Tran" . "txgvnn@gmail.com") (:keywords "comm" "docs" "tools" "terraform") (:url . "https://github.com/TxGVNN/terraform-doc"))]) (terraform-mode . [(20240321 2136) ((emacs (24 3)) (hcl-mode (0 3)) (dash (2 17 0))) "Major mode for terraform configuration file" tar ((:commit . "a645c32a8f0f0d04034262ae5fea330d5c7a33c6") (:authors ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainers ("Syohei YOSHIDA" . "syohex@gmail.com")) (:maintainer "Syohei YOSHIDA" . "syohex@gmail.com") (:url . "https://github.com/syohex/emacs-terraform-mode"))]) (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"))]) @@ -5156,7 +5151,7 @@ (textx-mode . [(20230324 2020) ((emacs (24 3))) "Major mode for editing TextX files" tar ((:commit . "ecf90abec508cfd82d5da68474e976be907d9a77") (:authors ("Novak Boškov" . "gnovak.boskov@gmail.com")) (:maintainers ("Novak Boškov" . "gnovak.boskov@gmail.com")) (:maintainer "Novak Boškov" . "gnovak.boskov@gmail.com") (:keywords "textx") (:url . "https://github.com/novakboskov/textx-mode"))]) (tf2-conf-mode . [(20161209 1620) nil "TF2 Configuration files syntax highlighting" tar ((:commit . "94c971da4a78d55da2848d1e76d513e5e0a8f7eb") (:authors ("Guillermo Robles" . "guillerobles1995@gmail.com")) (:maintainers ("Guillermo Robles" . "guillerobles1995@gmail.com")) (:maintainer "Guillermo Robles" . "guillerobles1995@gmail.com") (:keywords "languages") (:url . "https://github.com/wynro/emacs-tf2-conf-mode"))]) (tfsmacs . [(20180911 2114) ((emacs (25)) (tablist (0 70))) "MS TFS source control interaction." tar ((:commit . "13ee3f528ff616880611f563a68d921250692ef8") (:authors ("Dino Chiesa <dpchiesa@outlook.com>, Sebastian Monia" . "smonia@outlook.com")) (:maintainers ("Dino Chiesa <dpchiesa@outlook.com>, Sebastian Monia" . "smonia@outlook.com")) (:maintainer "Dino Chiesa <dpchiesa@outlook.com>, Sebastian Monia" . "smonia@outlook.com") (:keywords "tfs" "vc") (:url . "http://github.com/sebasmonia/tfsmacs/"))]) - (the-matrix-theme . [(20240214 747) ((emacs (26 1))) "Green-on-black dark theme inspired by \"The Matrix\" movie" tar ((:commit . "1cfaa2b20d1dd9253b0654284eaff41e5a73d444") (:authors ("Dan Dee" . "monkeyjunglejuice@pm.me")) (:maintainers ("Dan Dee" . "monkeyjunglejuice@pm.me")) (:maintainer "Dan Dee" . "monkeyjunglejuice@pm.me") (:keywords "faces" "theme") (:url . "https://github.com/monkeyjunglejuice/matrix-emacs-theme"))]) + (the-matrix-theme . [(20240402 2348) ((emacs (26 1))) "Green-on-black dark theme inspired by \"The Matrix\" movie" tar ((:commit . "060584cd83cc70c563582cd13f096c82ff462f2d") (:authors ("Dan Dee" . "monkeyjunglejuice@pm.me")) (:maintainers ("Dan Dee" . "monkeyjunglejuice@pm.me")) (:maintainer "Dan Dee" . "monkeyjunglejuice@pm.me") (:keywords "faces" "theme") (:url . "https://github.com/monkeyjunglejuice/matrix-emacs-theme"))]) (theme-anchor . [(20230924 2041) ((emacs (26))) "Apply theme in current buffer only" tar ((:commit . "dd69fe04d901e771cafde3992042a212e4a62620") (:authors ("Liāu, Kiong-Gē" . "gliao.tw@pm.me")) (:maintainers ("Liāu, Kiong-Gē" . "gliao.tw@pm.me")) (:maintainer "Liāu, Kiong-Gē" . "gliao.tw@pm.me") (:keywords "extensions" "lisp" "theme") (:url . "https://github.com/GongYiLiao/theme-anchor"))]) (theme-changer . [(20230904 1706) ((cl-lib (0))) "Sunrise/Sunset Theme Changer for Emacs" tar ((:commit . "7febd7632451bb99a5d92f24623432c4de035ff1") (:authors ("Joshua B. Griffith" . "josh.griffith@gmail.com")) (:maintainers ("Samuel W. Flint" . "swflint@flintfam.org")) (:maintainer "Samuel W. Flint" . "swflint@flintfam.org") (:keywords "color-theme" "deftheme" "solar" "sunrise" "sunset") (:url . "https://github.com/hadronzoo/theme-changer"))]) (theme-looper . [(20210827 424) ((emacs (24)) (cl-lib (0 5))) "A package for switching themes in Emacs interactively" tar ((:commit . "e6e8efd740df0b68db89805ba72492818dba61ab") (: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" "color-themes") (:url . "http://ismail.teamfluxion.com"))]) @@ -5167,7 +5162,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 . [(20240325 1231) ((emacs (24))) "major mode for fbthrift and Apache Thrift files" tar ((:commit . "5787aa9aff49ade3dab8b7464b933c058dea286b") (:keywords "languages"))]) + (thrift . [(20240401 1201) ((emacs (24))) "major mode for fbthrift and Apache Thrift files" tar ((:commit . "1de93f4bd143c4187c0313adbfbf672cf68b8b36") (: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 . [(20230312 1245) ((haskell-mode (16)) (emacs (25 1))) "Interact with TidalCycles for live coding patterns" tar ((:commit . "3ac320020d809ea626fc283839fecb10c9da4ce2") (:authors (nil . "alex@slab.org")) (:maintainers (nil . "alex@slab.org")) (:maintainer nil . "alex@slab.org") (:keywords "tools") (:url . "https://github.com/tidalcycles/Tidal"))]) @@ -5185,7 +5180,7 @@ (timp . [(20160618 803) ((emacs (24 4)) (cl-lib (0 5)) (fifo-class (1 0)) (signal (1 0))) "Multithreading library" tar ((:commit . "59657bf603904635d88c3fe4ff1ce45ee6572428") (:authors ("Mola-T" . "Mola@molamola.xyz")) (:maintainers ("Mola-T" . "Mola@molamola.xyz")) (:maintainer "Mola-T" . "Mola@molamola.xyz") (:keywords "internal" "lisp" "processes" "tools") (:url . "https://github.com/mola-T/timp"))]) (timu-caribbean-theme . [(20240224 2028) ((emacs (27 1))) "Color theme with cyan as a dominant color" tar ((:commit . "ec83fc030ad7c098637df6f3a56d844f8526c571") (:authors ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainers ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainer "Aimé Bertrand" . "aime.bertrand@macowners.club") (:keywords "faces" "themes") (:url . "https://gitlab.com/aimebertrand/timu-caribbean-theme"))]) (timu-line . [(20231002 1016) ((emacs (28 1)) (f (0 20 0))) "Custom and simple mode line" tar ((:commit . "836cb92063076981f93e44f72dccd46a37e96785") (:authors ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainers ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainer "Aimé Bertrand" . "aime.bertrand@macowners.club") (:keywords "modeline" "frames" "ui") (:url . "https://gitlab.com/aimebertrand/timu-line"))]) - (timu-macos-theme . [(20240317 2007) ((emacs (27 1))) "Color theme inspired by the macOS UI" tar ((:commit . "6079fa1bf9859955d30d6c51e7d8105588a9b588") (:authors ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainers ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainer "Aimé Bertrand" . "aime.bertrand@macowners.club") (:keywords "faces" "themes") (:url . "https://gitlab.com/aimebertrand/timu-macos-theme"))]) + (timu-macos-theme . [(20240401 1346) ((emacs (27 1))) "Color theme inspired by the macOS UI" tar ((:commit . "7734bd7287fa18cad8c146beabd52b52444e9da3") (:authors ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainers ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainer "Aimé Bertrand" . "aime.bertrand@macowners.club") (:keywords "faces" "themes") (:url . "https://gitlab.com/aimebertrand/timu-macos-theme"))]) (timu-rouge-theme . [(20240224 2040) ((emacs (27 1))) "Color theme inspired by the Rouge Theme for VSCode" tar ((:commit . "2095a2bedb2682145407e149d1c2b1c0aa02a6f8") (:authors ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainers ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainer "Aimé Bertrand" . "aime.bertrand@macowners.club") (:keywords "faces" "themes") (:url . "https://gitlab.com/aimebertrand/timu-rouge-theme"))]) (timu-spacegrey-theme . [(20240224 2010) ((emacs (26 1))) "Color theme inspired by the Spacegray theme in Sublime Text" tar ((:commit . "1cca501c9640a9f1d6bf717dc36df96d35deeeba") (:authors ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainers ("Aimé Bertrand" . "aime.bertrand@macowners.club")) (:maintainer "Aimé Bertrand" . "aime.bertrand@macowners.club") (:keywords "faces" "themes") (:url . "https://gitlab.com/aimebertrand/timu-spacegrey-theme"))]) (tinkerer . [(20200914 1756) ((s (1 2 0))) "Elisp wrapper for Tinkerer Blogging Engine." tar ((:commit . "7cedeb264a44cd62bcd9c778dca52316d09e07e5") (:authors ("Yagnesh Raghava Yakkala" . "hi@yagnesh.org")) (:maintainers ("Yagnesh Raghava Yakkala" . "hi@yagnesh.org")) (:maintainer "Yagnesh Raghava Yakkala" . "hi@yagnesh.org") (:keywords "tinkerer" "blog" "wrapper") (:url . "https://github.com/yyr/tinkerer.el"))]) @@ -5270,7 +5265,7 @@ (treemacs-projectile . [(20240131 2042) ((emacs (26 1)) (projectile (0 14 0)) (treemacs (0 0))) "Projectile integration for treemacs" tar ((:commit . "bcba09c1581c4bd93ff0217d464aead04f6d26d4") (: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"))]) (treemacs-tab-bar . [(20240131 2042) ((emacs (27 1)) (treemacs (0 0)) (dash (2 11 0))) "Tab bar integration for treemacs" tar ((:commit . "bcba09c1581c4bd93ff0217d464aead04f6d26d4") (:authors ("Alexander Miller" . "alexanderm@web.de") ("Jason Dufair" . "jase@dufair.org") ("Aaron Jensen" . "aaronjensen@gmail.com")) (:maintainers ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/treemacs"))]) (treepy . [(20230715 2154) ((emacs (25 1))) "Generic tree traversal tools" tar ((:commit . "75fe3ec37e6f9b2bdfd6d0584efd984d0c00a43e") (:authors ("Daniel Barreto" . "daniel.barreto.n@gmail.com")) (:maintainers ("Daniel Barreto" . "daniel.barreto.n@gmail.com")) (:maintainer "Daniel Barreto" . "daniel.barreto.n@gmail.com") (:keywords "lisp" "maint" "tools") (:url . "https://github.com/volrath/treepy.el"))]) - (treesit-auto . [(20240223 1450) ((emacs (29 0))) "Automatically use tree-sitter enhanced major modes" tar ((:commit . "299dd88c9e5f1ff91a8f1989ad1e97e86c220020") (:authors ("Robb Enzmann" . "robbenzmann@gmail.com")) (:maintainers ("Robb Enzmann" . "robbenzmann@gmail.com")) (:maintainer "Robb Enzmann" . "robbenzmann@gmail.com") (:keywords "treesitter" "auto" "automatic" "major" "mode" "fallback" "convenience") (:url . "https://github.com/renzmann/treesit-auto.git"))]) + (treesit-auto . [(20240401 1240) ((emacs (29 0))) "Automatically use tree-sitter enhanced major modes" tar ((:commit . "b5fcf8e5515c5c5787073c1bc3f6f2bf5bfb1cf1") (:authors ("Robb Enzmann" . "robbenzmann@gmail.com")) (:maintainers ("Robb Enzmann" . "robbenzmann@gmail.com")) (:maintainer "Robb Enzmann" . "robbenzmann@gmail.com") (:keywords "treesitter" "auto" "automatic" "major" "mode" "fallback" "convenience") (:url . "https://github.com/renzmann/treesit-auto.git"))]) (treeview . [(20230728 2343) ((emacs (24 4))) "A generic tree navigation library" tar ((:commit . "c6888e5f3aa0d72a7b4db625fcc2a847fd3bb1ce") (:authors ("Tilman Rassy" . "tilman.rassy@googlemail.com")) (:maintainers ("Tilman Rassy" . "tilman.rassy@googlemail.com")) (:maintainer "Tilman Rassy" . "tilman.rassy@googlemail.com") (:keywords "lisp" "tools" "internal" "convenience") (:url . "https://github.com/tilmanrassy/emacs-treeview"))]) (trident-mode . [(20190410 2036) ((emacs (24)) (slime (20130526)) (skewer-mode (1 5 0)) (dash (1 0 3))) "Live Parenscript interaction" tar ((:commit . "109a1bc10bd0c4b47679a6ca5c4cd27c7c8d4ccb") (:authors ("John Mastro" . "john.b.mastro@gmail.com")) (:maintainers ("John Mastro" . "john.b.mastro@gmail.com")) (:maintainer "John Mastro" . "john.b.mastro@gmail.com") (:keywords "languages" "lisp" "processes" "tools") (:url . "https://github.com/johnmastro/trident-mode.el"))]) (trinary . [(20230301 2044) ((emacs (24))) "Trinary logic" tar ((:commit . "d4869d260f22d13a9a71327a6d40edc6980d022e") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainers ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:keywords "languages") (:url . "https://github.com/emacs-elsa/trinary-logic"))]) @@ -5306,7 +5301,7 @@ (typit . [(20220909 1233) ((emacs (24 4)) (f (0 18)) (mmt (0 1 1))) "Typing game similar to tests on 10 fast fingers" tar ((:commit . "6ad0d5a106c4a4428fd131653bbe7c0aab4b5f60") (:authors ("Mark Karpov" . "markkarpov92@gmail.com")) (:maintainers ("Mark Karpov" . "markkarpov92@gmail.com")) (:maintainer "Mark Karpov" . "markkarpov92@gmail.com") (:keywords "games") (:url . "https://github.com/mrkkrp/typit"))]) (typo . [(20200706 1714) nil "Minor mode for typographic editing" tar ((:commit . "173ebe4fc7ac38f344b16e6eaf41f79e38f20d57") (:authors ("Jorgen Schaefer" . "forcer@forcix.cx")) (:maintainers ("Jorgen Schaefer" . "forcer@forcix.cx")) (:maintainer "Jorgen Schaefer" . "forcer@forcix.cx") (:keywords "convenience" "wp") (:url . "https://github.com/jorgenschaefer/typoel"))]) (typo-suggest . [(20200830 1143) ((emacs (24 3)) (helm (3 0)) (company (0 9 10)) (s (1 12 0)) (dash (2 13 0))) "Don't make typos with the help of helm and company" tar ((:commit . "3014d18ae2f0b6b857bb613f373e034c743f4d2e") (:authors ("Kadir Can Çetin" . "kadircancetin@gmail.com")) (:maintainers ("Kadir Can Çetin" . "kadircancetin@gmail.com")) (:maintainer "Kadir Can Çetin" . "kadircancetin@gmail.com") (:keywords "convenience" "wp") (:url . "https://github.com/kadircancetin/typo-suggest"))]) - (tzc . [(20230504 445) ((emacs (28 1))) "Converts time between different time zones" tar ((:commit . "e815b43790d9a517f89a2bb592c665bd911a4477") (:authors ("Md Arif Shaikh" . "arifshaikh.astro@gmail.com")) (:maintainers ("Md Arif Shaikh" . "arifshaikh.astro@gmail.com")) (:maintainer "Md Arif Shaikh" . "arifshaikh.astro@gmail.com") (:keywords "convenience") (:url . "https://github.com/md-arif-shaikh/tzc"))]) + (tzc . [(20240403 301) ((emacs (28 1))) "Converts time between different time zones" tar ((:commit . "2882c1c793f45e5cbbd5988af29079529ab4af0f") (:authors ("Md Arif Shaikh" . "arifshaikh.astro@gmail.com")) (:maintainers ("Md Arif Shaikh" . "arifshaikh.astro@gmail.com")) (:maintainer "Md Arif Shaikh" . "arifshaikh.astro@gmail.com") (:keywords "convenience") (:url . "https://github.com/md-arif-shaikh/tzc"))]) (ubuntu-theme . [(20150805 1506) nil "A theme inspired by the default terminal colors in Ubuntu" tar ((:commit . "88b0eefc75d4cbcde103057e1c5968d4c3052f69") (:authors ("Francesc Rocher" . "francesc.rocher@gmail.com")) (:maintainers ("Francesc Rocher" . "francesc.rocher@gmail.com")) (:maintainer "Francesc Rocher" . "francesc.rocher@gmail.com") (:url . "http://github.com/rocher/ubuntu-theme"))]) (uci-mode . [(20210626 1956) ((emacs (25 1))) "Major-mode for chess engine interaction" tar ((:commit . "2cdf4de5af96d56108a0a5716416ef3c8ac7bb7c") (:authors ("Dodge Coates and Roland Walker")) (:maintainers ("Dodge Coates and Roland Walker")) (:maintainer "Dodge Coates and Roland Walker") (:keywords "data" "games" "chess") (:url . "https://github.com/dwcoates/uci-mode"))]) (ucs-utils . [(20230119 2237) ((emacs (24 3)) (persistent-soft (0 8 10)) (pcache (0 5 1)) (list-utils (0 4 6))) "Utilities for Unicode characters" tar ((:commit . "91b9e0207fff5883383fd39c45ad5522e9b90e65") (:authors ("Roland Walker" . "walker@pobox.com")) (:maintainers ("Roland Walker" . "walker@pobox.com")) (:maintainer "Roland Walker" . "walker@pobox.com") (:keywords "i18n" "extensions") (:url . "http://github.com/rolandwalker/ucs-utils"))]) @@ -5407,7 +5402,7 @@ (vector-utils . [(20140508 2041) nil "Vector-manipulation utility functions" tar ((:commit . "5f9ced3960a318d611c3d20ffdc9ca74054fa8b7") (:authors ("Roland Walker" . "walker@pobox.com")) (:maintainers ("Roland Walker" . "walker@pobox.com")) (:maintainer "Roland Walker" . "walker@pobox.com") (:keywords "extensions") (:url . "http://github.com/rolandwalker/vector-utils"))]) (vega-view . [(20210401 1115) ((emacs (25)) (cider (0 24 0)) (parseedn (0 1))) "Vega visualization viewer" tar ((:commit . "3793025a523a86acc6255b4183b12ebfc95e1116") (:authors ("Jack Rusher" . "jack@appliedscience.studio")) (:maintainers ("Jack Rusher" . "jack@appliedscience.studio")) (:maintainer "Jack Rusher" . "jack@appliedscience.studio") (:keywords "multimedia") (:url . "https://www.github.com/applied-science/emacs-vega-view"))]) (vegetative-theme . [(20220822 353) ((autothemer (0 2)) (emacs (24))) "A Theme based on green CRT terminals" tar ((:commit . "db60ce0fe327ae7e4371545179ed94483b1132a8") (:url . "http://github.com/emacsfodder/emacs-theme-vegetative"))]) - (verb . [(20240317 1608) ((emacs (26 3))) "Organize and send HTTP requests" tar ((:commit . "a430847beb925ae82007d70f32f3bab38f0054e9") (:authors ("Federico Tedin" . "federicotedin@gmail.com")) (:maintainers ("Federico Tedin" . "federicotedin@gmail.com")) (:maintainer "Federico Tedin" . "federicotedin@gmail.com") (:keywords "tools") (:url . "https://github.com/federicotdn/verb"))]) + (verb . [(20240401 2214) ((emacs (26 3))) "Organize and send HTTP requests" tar ((:commit . "ac6c0ca3777431268e1f5a540c003aa16345381d") (:authors ("Federico Tedin" . "federicotedin@gmail.com")) (:maintainers ("Federico Tedin" . "federicotedin@gmail.com")) (:maintainer "Federico Tedin" . "federicotedin@gmail.com") (:keywords "tools") (:url . "https://github.com/federicotdn/verb"))]) (veri-kompass . [(20200213 934) ((emacs (25)) (cl-lib (0 5)) (org (8 2 0))) "verilog codebase navigation facility" tar ((:commit . "271903cdf92db05898ee7cffb65641f30fa08280") (:maintainers (nil . "andrea_corallo@yahoo.it")) (:maintainer nil . "andrea_corallo@yahoo.it") (:keywords "languages" "extensions" "verilog" "hardware" "rtl") (:url . "https://gitlab.com/koral/veri-kompass"))]) (verify-url . [(20160426 1228) ((cl-lib (0 5))) "find out invalid urls in the buffer or region" tar ((:commit . "d6f3623cda8cd526a2d198619b137059cb1ba1ab") (:authors ("DarkSun" . "lujun9972@gmail.com")) (:maintainers ("DarkSun" . "lujun9972@gmail.com")) (:maintainer "DarkSun" . "lujun9972@gmail.com") (:keywords "convenience" "usability" "url") (:url . "https://github.com/lujun9972/verify-url"))]) (verilog-ext . [(20240401 1210) ((emacs (29 1)) (verilog-mode (2024 3 1 121933719)) (verilog-ts-mode (0 1 2)) (lsp-mode (8 0 0)) (ag (0 48)) (ripgrep (0 4 0)) (hydra (0 15 0)) (apheleia (3 1)) (yasnippet (0 14 0)) (flycheck (32)) (outshine (3 0 1)) (async (1 9 7))) "SystemVerilog Extensions" tar ((:commit . "73c887aa125173c1d94e9d8b8008f372d7fe5e72") (:authors ("Gonzalo Larumbe" . "gonzalomlarumbe@gmail.com")) (:maintainers ("Gonzalo Larumbe" . "gonzalomlarumbe@gmail.com")) (:maintainer "Gonzalo Larumbe" . "gonzalomlarumbe@gmail.com") (:keywords "verilog" "ide" "tools") (:url . "https://github.com/gmlarumbe/verilog-ext"))]) diff --git a/emacs/elpa/archives/nongnu/archive-contents.signed b/emacs/elpa/archives/nongnu/archive-contents.signed @@ -1,2 +1,2 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasign@elpa.gnu.org> (trust undefined) created at 2024-04-01T10:05:07+0000 using RSA -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) <elpasign@elpa.gnu.org> (trust undefined) created at 2024-04-01T10:05:07+0000 using EDDSA -\ No newline at end of file +Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasign@elpa.gnu.org> (trust undefined) created at 2024-04-04T10:35:05+0000 using RSA +Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) <elpasign@elpa.gnu.org> (trust undefined) created at 2024-04-04T10:35:05+0000 using EDDSA +\ No newline at end of file diff --git a/emacs/elpa/ledger-mode-20240326.1834/dir b/emacs/elpa/ledger-mode-20240326.1834/dir @@ -1,18 +0,0 @@ -This is the file .../info/dir, which contains the -topmost node of the Info hierarchy, called (dir)Top. -The first time you invoke Info you start off looking at this node. - -File: dir, Node: Top This is the top of the INFO tree - - This (the Directory node) gives a menu of major topics. - Typing "q" exits, "H" lists all Info commands, "d" returns here, - "h" gives a primer for first-timers, - "mEmacs<Return>" visits the Emacs manual, etc. - - In Emacs, you can click mouse button 2 on a menu item or cross reference - to select it. - -* Menu: - -Emacs -* Ledger Mode: (ledger-mode). Command-Line Accounting diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-check.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-check.el @@ -1,146 +0,0 @@ -;;; ledger-check.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 Craig Earls (enderw88 AT gmail DOT com) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - -;;; Commentary: -;; Provide secial mode to correct errors in ledger when running with --strict and --explicit -;; -;; Adapted to ledger mode by Craig Earls <enderw88 at gmail dot com> - -;;; Code: - -(require 'easymenu) -(require 'ledger-navigate) -(require 'ledger-report) ; for ledger-master-file - - -(defvar ledger-check-buffer-name "*Ledger Check*") -(defvar-local ledger-check--original-window-configuration nil) - - - - -(defvar ledger-check-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "RET") #'ledger-report-visit-source) - (define-key map (kbd "q") #'ledger-check-quit) - map) - "Keymap for `ledger-check-mode'.") - -(easy-menu-define ledger-check-mode-menu ledger-check-mode-map - "Ledger check menu." - '("Check" - ;; ["Re-run Check" ledger-check-redo] - "---" - ["Visit Source" ledger-report-visit-source] - "---" - ["Quit" ledger-check-quit] - )) - -(define-derived-mode ledger-check-mode text-mode "Ledger-Check" - "A mode for viewing ledger errors and warnings.") - - -(defun ledger-do-check () - "Run a check command ." - (goto-char (point-min)) - (let ((data-pos (point)) - (have-warnings nil)) - (shell-command - ;; ledger balance command will just return empty if you give it - ;; an account name that doesn't exist. I will assume that no - ;; one will ever have an account named "e342asd2131". If - ;; someones does, this will probably still work for them. - ;; I should only highlight error and warning lines. - "ledger bal e342asd2131 --strict --explicit " - t nil) - (goto-char data-pos) - - ;; format check report to make it navigate the file - - (while (re-search-forward "^.*: \"\\(.*\\)\", line \\([0-9]+\\)" nil t) - (let ((file (match-string 1)) - (line (string-to-number (match-string 2)))) - (when file - (set-text-properties (line-beginning-position) (line-end-position) - (list 'ledger-source (cons file (save-window-excursion - (save-excursion - (find-file file) - (widen) - (ledger-navigate-to-line line) - (point-marker)))))) - (add-text-properties (line-beginning-position) (line-end-position) - (list 'font-lock-face 'ledger-font-report-clickable-face)) - (setq have-warnings 'true) - (end-of-line)))) - (if (not have-warnings) - (insert "No errors or warnings reported.")))) - -(defun ledger-check-goto () - "Goto the ledger check buffer." - (interactive) - (let ((rbuf (get-buffer ledger-check-buffer-name))) - (if (not rbuf) - (error "There is no ledger check buffer")) - (pop-to-buffer rbuf) - (shrink-window-if-larger-than-buffer))) - -(defun ledger-check-quit () - "Quit the ledger check buffer." - (interactive) - (ledger-check-goto) - (set-window-configuration ledger-check--original-window-configuration) - (kill-buffer (get-buffer ledger-check-buffer-name))) - -(defun ledger-check-buffer (&optional interactive) - "Check the current buffer for errors. - -Runs ledger with --explicit and --strict report errors and assist -with fixing them. - -The output buffer will be in `ledger-check-mode', which defines -commands for navigating the buffer to the errors found, etc. - -When INTERACTIVE is non-nil (i.e., when called interactively), -prompt to save if the current buffer is modified." - (interactive "p") - (when (and interactive - (buffer-modified-p) - (y-or-n-p "Buffer modified, save it? ")) - (save-buffer)) - (let ((_buf (find-file-noselect (ledger-master-file))) - (cbuf (get-buffer ledger-check-buffer-name)) - (wcfg (current-window-configuration))) - (if cbuf - (kill-buffer cbuf)) - (with-current-buffer - (pop-to-buffer (get-buffer-create ledger-check-buffer-name)) - (ledger-check-mode) - (setq ledger-check--original-window-configuration wcfg) - (ledger-do-check) - (shrink-window-if-larger-than-buffer) - (set-buffer-modified-p nil) - (setq buffer-read-only t) - (message "q to quit; r to redo; k to kill")))) - - -(provide 'ledger-check) - -;;; ledger-check.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-check.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-check.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-commodities.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-commodities.el @@ -1,160 +0,0 @@ -;;; ledger-commodities.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - -;;; Commentary: -;; Helper functions to deal with commoditized numbers. A commoditized -;; number will be a list of value and string where the string contains -;; the commodity - -;;; Code: - -(require 'ledger-regex) - -;; These keep the byte-compiler from warning about them, but have no other -;; effect: -(defvar ledger-environment-alist) -(declare-function ledger-exec-ledger "ledger-exec" (input-buffer &optional output-buffer &rest args)) - -(defcustom ledger-reconcile-default-commodity "$" - "The default commodity for use in target calculations in ledger reconcile." - :type 'string - :group 'ledger-reconcile) - -(defun ledger-read-commodity-with-prompt (prompt) - "Read commodity name after PROMPT. - -Default value is `ledger-reconcile-default-commodity'." - (let* ((buffer (current-buffer)) - (commodities (with-temp-buffer - (ledger-exec-ledger buffer (current-buffer) "commodities") - (split-string (buffer-string) "\n" t)))) - (completing-read prompt commodities nil t nil nil ledger-reconcile-default-commodity))) - -(defun ledger-split-commodity-string (str) - "Split a commoditized string, STR, into two parts. -Returns a list with (value commodity)." - (let ((number-regex (if (assoc "decimal-comma" ledger-environment-alist) - ledger-amount-decimal-comma-regex - ledger-amount-decimal-period-regex))) - (if (> (length str) 0) - (with-temp-buffer - (insert str) - (goto-char (point-min)) - (cond - ((re-search-forward "\"\\(.*\\)\"" nil t) ; look for quoted commodities - (let ((com (delete-and-extract-region - (match-beginning 1) - (match-end 1)))) - (if (re-search-forward - number-regex nil t) - (list - (ledger-string-to-number - (delete-and-extract-region (match-beginning 0) (match-end 0))) - com)))) - ((re-search-forward number-regex nil t) - ;; found a number in the current locale, return it in the - ;; car. Anything left over is annotation, the first - ;; thing should be the commodity, separated by - ;; whitespace, return it in the cdr. I can't think of - ;; any counterexamples - (list - (ledger-string-to-number - (delete-and-extract-region (match-beginning 0) (match-end 0))) - (nth 0 (split-string (buffer-substring-no-properties (point-min) (point-max)))))) - ((re-search-forward "0" nil t) - ;; couldn't find a decimal number, look for a single 0, - ;; indicating account with zero balance - (list 0 ledger-reconcile-default-commodity)) - ;; nothing found, return 0 - (t (list 0 ledger-reconcile-default-commodity))))))) - -(defun ledger-string-balance-to-commoditized-amount (str) - "Return a commoditized amount (val, \"comm\") from STR." - ; break any balances with multi commodities into a list - (mapcar #'(lambda (st) - (ledger-split-commodity-string st)) - (split-string str "[\n\r]"))) - -(defun ledger-subtract-commodity (c1 c2) - "Subtract C2 from C1, ensuring their commodities match." - (if (string= (cadr c1) (cadr c2)) - (list (-(car c1) (car c2)) (cadr c1)) - (error "Can't subtract different commodities %S from %S" c2 c1))) - -(defun ledger-add-commodity (c1 c2) - "Add C1 and C2, ensuring their commodities match." - (if (string= (cadr c1) (cadr c2)) - (list (+ (car c1) (car c2)) (cadr c1)) - (error "Can't add different commodities, %S to %S" c1 c2))) - -(defun ledger-strip (str char) - "Return STR with CHAR removed." - (replace-regexp-in-string char "" str)) - -(defun ledger-string-to-number (str &optional decimal-comma) - "Parse STR as a number and return that number. - -Improves builtin `string-to-number' by handling -internationalization, and return nil if number can't be parsed. -See `ledger-environment-alist' for DECIMAL-COMMA." - (let ((nstr (if (or decimal-comma - (assoc "decimal-comma" ledger-environment-alist)) - (ledger-strip str "[.]") - (ledger-strip str ",")))) - (while (string-match "," nstr) ;if there is a comma now, it is a decimal point - (setq nstr (replace-match "." nil nil nstr))) - (string-to-number nstr))) - -(defun ledger-number-to-string (n &optional decimal-comma) - "See `number-to-string' for N. -DECIMAL-COMMA is as documented in `ledger-environment-alist'." - (let ((str (number-to-string n))) - (when (or decimal-comma - (assoc "decimal-comma" ledger-environment-alist)) - (while (string-match "\\." str) - (setq str (replace-match "," nil nil str)))) - str)) - -(defun ledger-commodity-to-string (c1) - "Return string representing C1. -Single character commodities are placed ahead of the value, -longer ones are after the value." - (let ((str (ledger-number-to-string (car c1))) - (commodity (cadr c1))) - (if (> (length commodity) 1) - (concat str " " commodity) - (concat commodity " " str)))) - -(defun ledger-read-commodity-string (prompt) - "Read an amount from mini-buffer using PROMPT." - (let ((str (read-from-minibuffer - (concat prompt " (" ledger-reconcile-default-commodity "): "))) - comm) - (when (and (> (length str) 0) - (ledger-split-commodity-string str)) - (setq comm (ledger-split-commodity-string str)) - (if (cadr comm) - comm - (list (car comm) ledger-reconcile-default-commodity))))) - -(provide 'ledger-commodities) - -;;; ledger-commodities.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-commodities.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-commodities.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-complete.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-complete.el @@ -1,387 +0,0 @@ -;;; ledger-complete.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - -;;; Commentary: -;; Functions providing payee and account auto complete. - -(require 'cl-lib) -(eval-when-compile - (require 'subr-x)) - -;; In-place completion support - -;;; Code: -(require 'ledger-context) -(require 'ledger-xact) -(require 'ledger-schedule) - -(defcustom ledger-accounts-file nil - "The path to an optional file in which all accounts are used or declared. -This file will then be used as a source for account name -completions instead of the current file. -See ledger's -\"account\" directive." - :type '(choice (const :tag "Use current buffer for completion" nil) - file) - :group 'ledger - :safe #'string-or-null-p) - -(defcustom ledger-payees-file nil - "The path to an optional file in which all payees are used or declared. -This file will then be used as a source for payee name -completions instead of the current file. -See ledger's \"payee\" directive." - :type '(choice (const :tag "Use current buffer for completion" nil) - file) - :group 'ledger - :safe #'string-or-null-p) - -(defcustom ledger-accounts-exclude-function nil - "Function to exclude accounts from completion. -Should be a predicate function that accepts one argument, an -element of `ledger-accounts-list-in-buffer'." - :type '(choice (const :tag "Do not exclude any accounts from completion" nil) - function) - :group 'ledger - :package-version '(ledger-mode . "2019-08-14")) - -(defcustom ledger-complete-in-steps nil - "When non-nil, `ledger-complete-at-point' completes account names in steps. -If nil, full account names are offered for completion." - :type 'boolean - :group 'ledger - :package-version '(ledger-mode . "4.0.0")) - -(defun ledger-parse-arguments () - "Parse whitespace separated arguments in the current region." - ;; FIXME: We don't use pcomplete anymore. - ;; This is more complex than it appears - ;; to need, so that it can work with pcomplete. See - ;; pcomplete-parse-arguments-function for details - (let* ((begin (save-match-data - (if (looking-back (concat "^\\(" ledger-iso-date-regexp "=\\|\\)" - ledger-incomplete-date-regexp) nil) - (match-end 1) - (save-excursion - (ledger-thing-at-point) ;; leave point at beginning of thing under point - (point))))) - (end (point)) - begins args) - ;; to support end of line metadata - (save-excursion - (when (search-backward ";" - (line-beginning-position) t) - (setq begin (match-beginning 0)))) - (save-excursion - (goto-char begin) - (when (< (point) end) - (skip-chars-forward " \t\n") - (setq begins (cons (point) begins)) - (setq args (cons (buffer-substring-no-properties - (car begins) end) - args))) - (cons (reverse args) (reverse begins))))) - - -(defun ledger-payees-in-buffer () - "Scan buffer and return list of all payees." - (let ((origin (point)) - payees-list) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward ledger-payee-name-or-directive-regex nil t) - (unless (and (>= origin (match-beginning 0)) - (< origin (match-end 0))) - (push (or (match-string-no-properties 1) (match-string-no-properties 2)) - payees-list)))) - ;; to the list - (sort (delete-dups payees-list) #'string-lessp))) - -(defun ledger-payees-list () - "Return a list of all known account names as strings. -Looks in `ledger-payees-file' if set, otherwise the current buffer." - (if ledger-payees-file - (let ((f ledger-payees-file)) - (with-temp-buffer - (insert-file-contents f) - (ledger-payees-in-buffer))) - (ledger-payees-in-buffer))) - -(defun ledger-accounts-in-buffer () - "Return an alist of accounts in the current buffer. -The `car' of each element is the account name and the `cdr' is an -alist where the key is a subdirective such as \"assert\" and the -value (if any) is the associated data. In other words, if you've -declared an account like so: - -account Assets:Checking - assert commodity == \"$\" - default - -Then one of the elements this function returns will be -\(\"Assets:Checking\" - (\"default\") - (\"assert\" . \"commodity == \"$\"\"))" - (save-excursion - (goto-char (point-min)) - (let (account-list - (seen (make-hash-table :test #'equal :size 1))) - ;; First, consider accounts declared with "account" directives, which may or - ;; may not have associated data. The data is on the following lines up to a - ;; line not starting with whitespace. - (while (re-search-forward ledger-account-directive-regex nil t) - (let ((account (match-string-no-properties 1)) - (lines (buffer-substring-no-properties - (point) - (progn (ledger-navigate-next-xact-or-directive) - (point)))) - data) - (dolist (d (split-string lines "\n")) - (setq d (string-trim d)) - (unless (string= d "") - (if (string-match " " d) - (push (cons (substring d 0 (match-beginning 0)) - (substring d (match-end 0) nil)) - data) - (push (cons d nil) data)))) - (push (cons account data) account-list) - (puthash account t seen))) - ;; Next, gather all accounts declared in postings - (unless - ;; FIXME: People who have set `ledger-flymake-be-pedantic' to non-nil - ;; probably don't want accounts from postings, just those declared - ;; with directives. But the name is a little misleading. Should we - ;; make a ledger-mode-be-pedantic and use that instead? - (bound-and-true-p ledger-flymake-be-pedantic) - (ledger-xact-iterate-transactions - (lambda (_pos _date _state _payee) - (let ((end (save-excursion (ledger-navigate-end-of-xact)))) - (forward-line) - (while (re-search-forward ledger-account-any-status-regex end t) - (let ((account (match-string-no-properties 1))) - (unless (gethash account seen) - (puthash account t seen) - (push (cons account nil) account-list)))))))) - (sort account-list (lambda (a b) (string-lessp (car a) (car b))))))) - -(defun ledger-accounts-list-in-buffer () - "Return a list of all known account names in the current buffer as strings. -Considers both accounts listed in postings and those declared -with \"account\" directives." - (let ((accounts (ledger-accounts-in-buffer))) - (when ledger-accounts-exclude-function - (setq accounts (cl-remove-if ledger-accounts-exclude-function accounts))) - (mapcar #'car accounts))) - -(defun ledger-accounts-list () - "Return a list of all known account names as strings. -Looks in `ledger-accounts-file' if set, otherwise the current buffer." - (if ledger-accounts-file - (let ((f ledger-accounts-file)) - (with-temp-buffer - (insert-file-contents f) - (ledger-accounts-list-in-buffer))) - (ledger-accounts-list-in-buffer))) - -(defun ledger-find-accounts-in-buffer () - (let ((account-tree (list t)) - (account-elements nil)) - (save-excursion - (goto-char (point-min)) - - (dolist (account (ledger-accounts-list)) - (let ((root account-tree)) - (setq account-elements - (split-string - account ":")) - (while account-elements - (let ((xact (assoc (car account-elements) root))) - (if xact - (setq root (cdr xact)) - (setq xact (cons (car account-elements) (list t))) - (nconc root (list xact)) - (setq root (cdr xact)))) - (setq account-elements (cdr account-elements)))))) - account-tree)) - -(defun ledger-accounts-tree () - "Return a tree of all accounts in the buffer." - (let* ((current (caar (ledger-parse-arguments))) - (elements (and current (split-string current ":"))) - (root (ledger-find-accounts-in-buffer)) - (prefix nil)) - (while (cdr elements) - (let ((xact (assoc (car elements) root))) - (if xact - (setq prefix (concat prefix (and prefix ":") - (car elements)) - root (cdr xact)) - (setq root nil elements nil))) - (setq elements (cdr elements))) - (setq root (delete (list (car elements) t) root)) - (and root - (sort - (mapcar (function - (lambda (x) - (let ((term (if prefix - (concat prefix ":" (car x)) - (car x)))) - (if (> (length (cdr x)) 1) - (concat term ":") - term)))) - (cdr root)) - 'string-lessp)))) - -(defun ledger-complete-date (month-string day-string) - "Complete a date." - (let* - ((now (current-time)) - (decoded (decode-time now)) - (this-month (nth 4 decoded)) - (this-year (nth 5 decoded)) - (last-month (if (> this-month 1) (1- this-month) 12)) - (last-year (1- this-year)) - (last-month-year (if (> this-month 1) this-year last-year)) - (month (and month-string - (string-to-number month-string))) - (day (string-to-number day-string)) - (dates (list (encode-time 0 0 0 day (or month this-month) this-year) - (if month - (encode-time 0 0 0 day month last-year) - (encode-time 0 0 0 day last-month last-month-year))))) - (lambda (_string _predicate _all) - (concat (ledger-format-date - (cl-find-if (lambda (date) (not (time-less-p now date))) dates)) - (and (= (point) (line-end-position)) " "))))) - -(defun ledger-complete-effective-date - (tx-year-string tx-month-string tx-day-string - month-string day-string) - "Complete an effective date." - (let* - ((tx-year (string-to-number tx-year-string)) - (tx-month (string-to-number tx-month-string)) - (tx-day (string-to-number tx-day-string)) - (tx-date (encode-time 0 0 0 tx-day tx-month tx-year)) - (next-month (if (< tx-month 12) (1+ tx-month) 1)) - (next-year (1+ tx-year)) - (next-month-year (if (< tx-month 12) tx-year next-year)) - (month (and month-string - (string-to-number month-string))) - (day (string-to-number day-string)) - (dates (list (encode-time 0 0 0 day (or month tx-month) tx-year) - (if month - (encode-time 0 0 0 day month next-year) - (encode-time 0 0 0 day next-month next-month-year))))) - (lambda (_string _predicate _all) - (concat (ledger-format-date - (cl-find-if (lambda (date) (not (time-less-p date tx-date))) dates)) - (and (= (point) (line-end-position)) " "))))) - -(defun ledger-complete-at-point () - "Do appropriate completion for the thing at point." - (let ((end (point)) - start collection - realign-after - delete-suffix) - (cond (;; Date - (looking-back (concat "^" ledger-incomplete-date-regexp) (line-beginning-position)) - (setq collection (ledger-complete-date (match-string 1) (match-string 2)) - start (match-beginning 0) - delete-suffix (save-match-data - (when (looking-at (rx (one-or-more (or digit (any ?/ ?-))))) - (length (match-string 0)))))) - (;; Effective dates - (looking-back (concat "^" ledger-iso-date-regexp "=" ledger-incomplete-date-regexp) - (line-beginning-position)) - (setq start (line-beginning-position)) - (setq collection (ledger-complete-effective-date - (match-string 2) (match-string 3) (match-string 4) - (match-string 5) (match-string 6)))) - (;; Payees - (eq (save-excursion (ledger-thing-at-point)) 'transaction) - (setq start (save-excursion (backward-word) (point))) - (setq collection #'ledger-payees-list)) - (;; Accounts - (save-excursion - (back-to-indentation) - (skip-chars-forward "([") ;; for virtual accounts - (setq start (point))) - (setq delete-suffix (save-excursion - (when (search-forward-regexp (rx (or eol (or ?\t (repeat 2 space)))) (line-end-position) t) - (- (match-beginning 0) end))) - realign-after t - collection (if ledger-complete-in-steps - #'ledger-accounts-tree - #'ledger-accounts-list)))) - (when collection - (let ((prefix (buffer-substring-no-properties start end))) - (list start end - (if (functionp collection) - (completion-table-with-cache - (lambda (_) - (cl-remove-if (apply-partially 'string= prefix) (funcall collection)))) - collection) - :exit-function (lambda (&rest _) - (when delete-suffix - (delete-char delete-suffix)) - (when (and realign-after ledger-post-auto-align) - (ledger-post-align-postings (line-beginning-position) (line-end-position)))) - 'ignore))))) - -(defun ledger-trim-trailing-whitespace (str) - (replace-regexp-in-string "[ \t]*$" "" str)) - -(defun ledger-fully-complete-xact () - "Completes a transaction if there is another matching payee in the buffer. - -Interactively, if point is after a payee, complete the -transaction with the details from the last transaction to that -payee." - (interactive) - (let* ((name (ledger-trim-trailing-whitespace (caar (ledger-parse-arguments)))) - (rest-of-name name) - xacts) - (save-excursion - (when (eq 'transaction (ledger-thing-at-point)) - (delete-region (point) (+ (length name) (point))) - ;; Search backward for a matching payee - (when (re-search-backward - (concat "^[0-9/.=-]+\\(\\s-+\\*\\)?\\(\\s-+(.*?)\\)?\\s-+\\(.*" - (regexp-quote name) ".*\\)" ) nil t) - (setq rest-of-name (match-string 3)) - ;; Start copying the postings - (forward-line) - (setq xacts (buffer-substring-no-properties (point) (ledger-navigate-end-of-xact)))))) - ;; Insert rest-of-name and the postings - (save-excursion - (insert rest-of-name ?\n) - (insert xacts) - (unless (looking-at-p "\n\n") - (insert "\n"))) - (forward-line) - (goto-char (line-end-position)) - (when (re-search-backward "\\(\t\\| [ \t]\\)" nil t) - (goto-char (match-end 0))))) - -(provide 'ledger-complete) - -;;; ledger-complete.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-complete.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-complete.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-context.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-context.el @@ -1,215 +0,0 @@ -;;; ledger-context.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - - -;;; Commentary: -;; Provide facilities for reflection in ledger buffers - -;;; Code: - -(require 'ledger-regex) - -;; ledger-*-string constants are assembled in the -;; `ledger-single-line-config' macro to form the regex and list of -;; elements -(defconst ledger-indent-string "\\(^[ \t]+\\)") -(defconst ledger-status-string "\\(*\\|!\\)?") -(defconst ledger-account-string "[\\[(]?\\(.*?\\)[])]?") -(defconst ledger-separator-string "\\(\\s-\\s-+\\)") -(defconst ledger-amount-string ledger-amount-regexp) -(defconst ledger-commoditized-amount-string ledger-commoditized-amount-regexp) -(defconst ledger-cost-string ledger-cost-regexp) -(defconst ledger-balance-assertion-string ledger-balance-assertion-regexp) -(defconst ledger-comment-string "\\(?:[ \t]*\n\\)?[ \t]*;[ \t]*\\(.*?\\)") -(defconst ledger-nil-string "\\([ \t]+\\)") -(defconst ledger-date-string "^\\([0-9]\\{4\\}[/-][01]?[0-9][/-][0123]?[0-9]\\)\\(?:=[0-9]\\{4\\}[/-][01]?[0-9][/-][0123]?[0-9]\\)?") -(defconst ledger-code-string "\\((.*)\\)?") -(defconst ledger-payee-string "\\(.*[^[:space:]\n]\\)") - - -(defun ledger-get-regex-str (name) - "Get the ledger regex of type NAME." - (symbol-value (intern (concat "ledger-" (symbol-name name) "-string")))) - -(defun ledger-line-regex (elements) - "Get a regex to match ELEMENTS on a single line." - (concat (apply 'concat (mapcar 'ledger-get-regex-str elements)) "[ \t]*$")) - -(defmacro ledger-single-line-config (&rest elements) - "Take ELEMENTS and return regex and element list for use in context-at-point." - `(list (ledger-line-regex (quote ,elements)) (quote ,elements))) - -(defconst ledger-line-config - (list (list 'xact (list (ledger-single-line-config date nil status nil code nil payee comment) - (ledger-single-line-config date nil status nil code nil payee) - (ledger-single-line-config date nil status nil payee comment) - (ledger-single-line-config date nil status nil payee) - (ledger-single-line-config date nil code nil payee comment) - (ledger-single-line-config date nil code nil payee) - (ledger-single-line-config date nil payee comment) - (ledger-single-line-config date nil payee))) - (list 'acct-transaction (list (ledger-single-line-config indent comment) - (ledger-single-line-config indent status nil account separator commoditized-amount nil cost nil balance-assertion) - (ledger-single-line-config indent status nil account separator commoditized-amount nil balance-assertion) - (ledger-single-line-config indent status nil account separator commoditized-amount nil cost comment) - (ledger-single-line-config indent status nil account separator commoditized-amount nil cost) - (ledger-single-line-config indent status nil account separator commoditized-amount comment) - (ledger-single-line-config indent status nil account separator commoditized-amount) - (ledger-single-line-config indent status nil account separator amount) - (ledger-single-line-config indent status nil account comment) - (ledger-single-line-config indent status nil account) - (ledger-single-line-config indent account separator commoditized-amount comment) - (ledger-single-line-config indent account separator commoditized-amount) - (ledger-single-line-config indent account separator amount) - (ledger-single-line-config indent account comment) - (ledger-single-line-config indent account))))) - -(defun ledger-extract-context-info (line-type pos) - "Get context info for current line with LINE-TYPE. - -Assumes point is at beginning of line, and the POS argument specifies -where the \"users\" point was." - (let ((linfo (assoc line-type ledger-line-config)) - found field fields) - (dolist (re-info (nth 1 linfo)) - (let ((re (nth 0 re-info)) - (names (nth 1 re-info))) - (unless found - (when (looking-at re) - (setq found t) - (dotimes (i (length names)) - (when (nth i names) - (setq fields (append fields - (list - (list (nth i names) - (match-string-no-properties (1+ i)) - (match-beginning (1+ i)))))))) - (dolist (f fields) - (and (nth 1 f) - (>= pos (nth 2 f)) - (setq field (nth 0 f)))))))) - (list line-type field fields))) - -(defun ledger-thing-at-point () - "Describe thing at point. Return \='transaction, \='posting, \='day, or nil. - -Leave point at the beginning of the thing at point, otherwise do not move point." - (let ((here (point))) - (goto-char (line-beginning-position)) - (cond ((looking-at "^\\(?:[~=][ \t]\\|[0-9/.=-]+\\(\\s-+\\*\\)?\\(\\s-+(.+?)\\)?\\s-+\\)") - (goto-char (match-end 0)) - 'transaction) - ((looking-at "^\\s-+\\([*!]\\s-+\\)?[[(]?\\([^\\s-]\\)") - (goto-char (match-beginning 2)) - 'posting) - ((looking-at "^\\(sun\\|mon\\|tue\\|wed\\|thu\\|fri\\|sat\\)\\s-+") - (goto-char (match-end 0)) - 'day) - (t - (ignore (goto-char here)))))) - -(defun ledger-context-at-point () - "Return a list describing the context around point. - -The contents of the list are the line type, the name of the field -containing point, and for selected line types, the content of -the fields in the line in a association list." - (let ((pos (point))) - (save-excursion - (beginning-of-line) - (let ((first-char (char-after))) - (cond ((equal (point) (line-end-position)) - '(empty-line nil nil)) - ((memq first-char '(?\ ?\t)) - (ledger-extract-context-info 'acct-transaction pos)) - ((memq first-char '(?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)) - (ledger-extract-context-info 'xact pos)) - ((equal first-char ?\=) - '(automated-xact nil nil)) - ((equal first-char ?\~) - '(period-xact nil nil)) - ((equal first-char ?\!) - '(command-directive)) - ((equal first-char ?\;) - '(comment nil nil)) - ((equal first-char ?Y) - '(default-year nil nil)) - ((equal first-char ?P) - '(commodity-price nil nil)) - ((equal first-char ?N) - '(price-ignored-commodity nil nil)) - ((equal first-char ?D) - '(default-commodity nil nil)) - ((equal first-char ?C) - '(commodity-conversion nil nil)) - ((equal first-char ?i) - '(timeclock-i nil nil)) - ((equal first-char ?o) - '(timeclock-o nil nil)) - ((equal first-char ?b) - '(timeclock-b nil nil)) - ((equal first-char ?h) - '(timeclock-h nil nil)) - (t - '(unknown nil nil))))))) - -(defun ledger-context-other-line (offset) - "Return a list describing context of line OFFSET from existing position. - -Offset can be positive or negative. If run out of buffer before reaching -specified line, returns nil." - (save-excursion - (let ((left (forward-line offset))) - (if (not (equal left 0)) - nil - (ledger-context-at-point))))) - -(defun ledger-context-line-type (context-info) - (nth 0 context-info)) - -(defun ledger-context-current-field (context-info) - (nth 1 context-info)) - -(defun ledger-context-field-info (context-info field-name) - (assoc field-name (nth 2 context-info))) - -(defun ledger-context-field-present-p (context-info field-name) - (not (null (ledger-context-field-info context-info field-name)))) - -(defun ledger-context-field-value (context-info field-name) - (nth 1 (ledger-context-field-info context-info field-name))) - -(defun ledger-context-field-position (context-info field-name) - (nth 2 (ledger-context-field-info context-info field-name))) - -(defun ledger-context-field-end-position (context-info field-name) - (+ (ledger-context-field-position context-info field-name) - (length (ledger-context-field-value context-info field-name)))) - -(defun ledger-context-goto-field-start (context-info field-name) - (goto-char (ledger-context-field-position context-info field-name))) - -(defun ledger-context-goto-field-end (context-info field-name) - (goto-char (ledger-context-field-end-position context-info field-name))) - -(provide 'ledger-context) - -;;; ledger-context.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-context.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-context.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-exec.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-exec.el @@ -1,126 +0,0 @@ -;;; ledger-exec.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - - -;;; Commentary: -;; Code for executing ledger synchronously. - -;;; Code: - -(require 'ledger-init) ;for `ledger-default-date-format' - -(declare-function ledger-master-file "ledger-report" ()) - -(defconst ledger-version-needed "3.0.0" - "The version of ledger executable needed for interactive features.") - -(defvar ledger-works nil - "Non-nil if the ledger binary can support `ledger-mode' interactive features.") - -(defgroup ledger-exec nil - "Interface to the Ledger command-line accounting program." - :group 'ledger) - -(defcustom ledger-mode-should-check-version t - "Should Ledger-mode verify that the executable is working?" - :type 'boolean - :group 'ledger-exec) - -(defcustom ledger-binary-path "ledger" - "Path to the ledger executable." - :type 'file - :risky t - :group 'ledger-exec) - -(defun ledger-exec-handle-error (ledger-errfile) - "Deal with ledger errors contained in LEDGER-ERRFILE." - (with-current-buffer (get-buffer-create "*Ledger Error*") - (let ((buffer-read-only nil)) - (delete-region (point-min) (point-max)) - (insert-file-contents ledger-errfile)) - (view-mode) - (setq buffer-read-only t) - (current-buffer))) - -(defun ledger-exec-success-p (exit-code ledger-output-buffer) - "Return non-nil if EXIT-CODE and LEDGER-OUTPUT-BUFFER indicate success." - (and (zerop exit-code) - (with-current-buffer ledger-output-buffer - (goto-char (point-min)) - (not (and (> (buffer-size) 1) (looking-at (regexp-quote "While"))))))) - -(defun ledger-exec-ledger (input-buffer &optional output-buffer &rest args) - "Run Ledger using INPUT-BUFFER. -Optionally capture output in OUTPUT-BUFFER, and pass ARGS on the -command line. Returns OUTPUT-BUFFER if ledger succeeded, -otherwise the error output is displayed and an error is raised." - (unless (and ledger-binary-path - (or (and (file-exists-p ledger-binary-path) - (file-executable-p ledger-binary-path)) - (executable-find ledger-binary-path))) - (error "`ledger-binary-path' (value: %s) is not executable" ledger-binary-path)) - (let ((buf (or input-buffer (find-file-noselect (ledger-master-file)))) - (outbuf (or output-buffer - (generate-new-buffer " *ledger-tmp*"))) - (errfile (make-temp-file "ledger-errors"))) - (unwind-protect - (with-current-buffer buf - (let ((exit-code - (let ((coding-system-for-write 'utf-8) - (coding-system-for-read 'utf-8)) - (apply #'call-process-region - (append (list (point-min) (point-max) - ledger-binary-path nil (list outbuf errfile) nil "-f" "-") - (list "--date-format" ledger-default-date-format) - args))))) - (if (ledger-exec-success-p exit-code outbuf) - outbuf - (display-buffer (ledger-exec-handle-error errfile)) - (error "Ledger execution failed")))) - (delete-file errfile)))) - -(defun ledger-version-greater-p (needed) - "Verify the ledger binary version is at least NEEDED." - (let ((version-strings '())) - (with-temp-buffer - (when (ledger-exec-ledger (current-buffer) (current-buffer) "--version") - (goto-char (point-min)) - (delete-horizontal-space) - (setq version-strings (split-string - (buffer-substring-no-properties (point) - (point-max)))) - (if (and (string-match (regexp-quote "Ledger") (car version-strings)) - (or (string= needed (cadr version-strings)) - (string< needed (cadr version-strings)))) - t ;; success - nil))))) ;;failure - -(defun ledger-check-version () - "Verify that ledger works and is modern enough." - (interactive) - (if ledger-mode-should-check-version - (if (setq ledger-works (ledger-version-greater-p ledger-version-needed)) - (message "Good Ledger Version") - (message "Bad Ledger Version")))) - -(provide 'ledger-exec) - -;;; ledger-exec.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-exec.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-exec.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-flymake.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-flymake.el @@ -1,146 +0,0 @@ -;;; ledger-flymake.el --- A ledger Flymake backend -*- lexical-binding: t; -*- - -;; Copyright (C) 2018 J. Alexander Branham (alex DOT branham AT gmail DOT com) - -;; This file is not part of GNU Emacs. - -;; This 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, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - -;;; Commentary: -;; Flymake is the built-in Emacs package to support on-the-fly syntax checking. -;; This file adds support for flymake to `ledger-mode'. Enable it by calling -;; `ledger-flymake-enable' from a file-visiting ledger buffer. To enable it -;; automatically, put this in your .emacs: -;; -;; (add-hook 'ledger-mode-hook #'ledger-flymake-enable) - -;;; Code: -(require 'cl-lib) -(require 'flymake) -(require 'ledger-exec) ; for `ledger-binary-path' -(require 'ledger-report) ; for `ledger-master-file' - -;; To silence byte compiler warnings in Emacs 25 and older: -(declare-function flymake-diag-region "flymake" (buffer line &optional col)) -(declare-function flymake-make-diagnostic "flymake" (buffer beg end type text &optional data overlay-properties)) - -(defvar-local ledger--flymake-proc nil) - -(defcustom ledger-flymake-be-pedantic nil - "If non-nil, pass the --pedantic flag for ledger to the flymake backend. -If --pedantic is in your ledgerrc file, then --pedantic gets -passed regardless of the value." - :type 'boolean - :package-version '(ledger-mode . "4.0.0") - :group 'ledger) - -(defcustom ledger-flymake-be-explicit nil - "If non-nil, pass the --explicit flag for ledger to the flymake backend. -If --explicit is in your ledgerrc file, then --explicit gets -passed regardless of the value." - :type 'boolean - :package-version '(ledger-mode . "4.0.0") - :group 'ledger) - -;; Based on the example from Flymake's info: -(defun ledger-flymake (report-fn &rest _args) - "A Flymake backend for `ledger-mode'. - -Flymake calls this with REPORT-FN as needed." - (unless (executable-find ledger-binary-path) - (error "Cannot find ledger")) - ;; If a live process launched in an earlier check was found, that - ;; process is killed. When that process's sentinel eventually runs, - ;; it will notice its obsoletion, since it have since reset - ;; `ledger-flymake-proc' to a different value - (when (process-live-p ledger--flymake-proc) - (kill-process ledger--flymake-proc)) - ;; Save the current buffer, the narrowing restriction, remove any - ;; narrowing restriction. - (let* ((source (current-buffer)) - (file (or (ledger-master-file) (buffer-file-name)))) - (save-restriction - (widen) - ;; Reset the `ledger--flymake-proc' process to a new process - ;; calling the ledger tool. - (setq - ledger--flymake-proc - (make-process - :name "ledger-flymake" :noquery t :connection-type 'pipe - :buffer (generate-new-buffer " *ledger-flymake*") - :command (cl-remove - nil - `(,ledger-binary-path "-f" ,file - ,(when ledger-flymake-be-pedantic "--pedantic") - ,(when ledger-flymake-be-explicit "--explicit") - "balance")) - :sentinel - (lambda (proc _event) - ;; Check that the process has indeed exited, as it might - ;; be simply suspended. - (when (eq 'exit (process-status proc)) - (unwind-protect - ;; Only proceed if `proc' is the same as - ;; `ledger--flymake-proc', which indicates that - ;; `proc' is not an obsolete process. - (if (with-current-buffer source (eq proc ledger--flymake-proc)) - (with-current-buffer (process-buffer proc) - (goto-char (point-min)) - ;; Parse the output buffer for diagnostic's - ;; messages and locations, collect them in a list - ;; of objects, and call `report-fn'. - (cl-loop - while (search-forward-regexp - ;; This regex needs to match the whole error. We - ;; also need a capture group for the error message - ;; (that's group 1 here) and the line number - ;; (group 2). - (rx line-start "While parsing file \"" (one-or-more (not whitespace)) " line " (group-n 2 (one-or-more num)) ":\n" - (zero-or-more line-start "While " (one-or-more not-newline) "\n" ) - (minimal-match (zero-or-more line-start (zero-or-more not-newline) "\n")) - (group-n 1 "Error: " (one-or-more not-newline) "\n")) - nil t) - for msg = (match-string 1) - for (beg . end) = (flymake-diag-region - source - (string-to-number (match-string 2))) - for type = :error - collect (flymake-make-diagnostic source - beg - end - type - msg) - into diags - finally (funcall report-fn diags))) - (flymake-log :warning "Canceling obsolete check %s" - proc)) - ;; Cleanup the temporary buffer used to hold the - ;; check's output. - (kill-buffer (process-buffer proc)))))))))) - -;;;###autoload -(defun ledger-flymake-enable () - "Enable `flymake-mode' in `ledger-mode' buffers." - (unless (> emacs-major-version 25) - (error "Ledger-flymake requires Emacs version 26 or higher")) - ;; Add `ledger-flymake' to `flymake-diagnostic-functions' so that flymake can - ;; work in ledger-mode: - (add-hook 'flymake-diagnostic-functions 'ledger-flymake nil t) - (flymake-mode)) - -(provide 'ledger-flymake) - -;;; ledger-flymake.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-flymake.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-flymake.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-fontify.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-fontify.el @@ -1,58 +0,0 @@ -;;; ledger-fontify.el --- Provide custom fontification for ledger-mode -*- lexical-binding: t; -*- - - -;; Copyright (C) 2014 Craig P. Earls (enderw88 at gmail dot com) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - -;;; Commentary: -;; Font-lock-mode doesn't handle multiline syntax very well. This -;; code provides font lock that is sensitive to overall transaction -;; states - - -;;; Code: - -(require 'ledger-navigate) -(require 'ledger-regex) -(require 'ledger-state) - -;; These are dynamically bound, see `font-lock-extend-region-functions'. -(defvar font-lock-beg) -(defvar font-lock-end) - -(defcustom ledger-fontify-xact-state-overrides nil - "If t the highlight entire xact with state." - :type 'boolean - :group 'ledger) - -(defun ledger-fontify-extend-region () - "Extend fontification region to include whole transactions or directives." - (save-match-data - (let* ((new-beg (min font-lock-beg (car (ledger-navigate-find-element-extents font-lock-beg)))) - (new-end (max font-lock-end (cadr (ledger-navigate-find-element-extents font-lock-end)))) - (changed (or (/= new-beg font-lock-beg) - (/= new-end font-lock-end)))) - (setq font-lock-beg new-beg) - (setq font-lock-end new-end) - changed))) - - -(provide 'ledger-fontify) - -;;; ledger-fontify.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-fontify.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-fontify.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-fonts.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-fonts.el @@ -1,684 +0,0 @@ -;;; ledger-fonts.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - - - -;;; Commentary: -;; All of the faces for ledger mode are defined here. - -;;; Code: - -(require 'ledger-navigate) -(require 'ledger-regex) -(require 'ledger-state) -(require 'ledger-fontify) - -(defgroup ledger-faces nil "Ledger mode highlighting" :group 'ledger) - -(defface ledger-font-auto-xact-face - `((t :inherit font-lock-negation-char-face)) - "Default face for automatic transactions" - :group 'ledger-faces) - -(defface ledger-font-periodic-xact-face - `((t :inherit font-lock-constant-face)) - "Default face for automatic transactions" - :group 'ledger-faces) - -(defface ledger-font-xact-cleared-face - `((t :inherit ledger-font-payee-cleared-face)) - "Default face for cleared transaction" - :group 'ledger-faces) - -(defface ledger-font-xact-pending-face - `((t :inherit ledger-font-pending-face)) - "Default face for pending transaction" - :group 'ledger-faces) - -(defface ledger-font-payee-uncleared-face - `((t :inherit error)) - "Default face for Ledger" - :group 'ledger-faces) - -(defface ledger-font-payee-cleared-face - `((t :inherit shadow)) - "Default face for cleared (*) payees" - :group 'ledger-faces) - -(defface ledger-font-payee-pending-face - `((t :inherit ledger-font-pending-face)) - "Default face for pending (!) payees" - :group 'ledger-faces) - -(defface ledger-font-xact-highlight-face - `((t - ,@(and (>= emacs-major-version 27) '(:extend t)) - :inherit ledger-occur-xact-face)) - "Default face for transaction under point" - :group 'ledger-faces) - -(defface ledger-font-pending-face - `((t :inherit warning)) - "Default face for pending (!) transactions" - :group 'ledger-faces) - -(defface ledger-font-other-face - `((t :inherit font-lock-type-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-directive-face - `((t :inherit font-lock-preprocessor-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-account-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-account-name-face - `((t :inherit font-lock-variable-name-face)) - "Face for account names in account and alias directives" - :group 'ledger-faces) - -(defface ledger-font-note-directive-face - `((t :inherit ledger-font-directive-face)) - "Face for note subdirectives" - :group 'ledger-faces) - -(defface ledger-font-note-text-face - `((t :inherit font-lock-doc-face)) - "Face for note subdirective text" - :group 'ledger-faces) - -(defface ledger-font-default-directive-face - `((t :inherit ledger-font-directive-face)) - "Face for default subdirectives" - :group 'ledger-faces) - -(defface ledger-font-price-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-price-date-face - `((t :inherit default)) - "Face for date and time in price directive" - :group 'ledger-faces) - -(defface ledger-font-price-symbol-face - `((t :inherit font-lock-constant-face)) - "Face for symbol in price directive" - :group 'ledger-faces) - -(defface ledger-font-price-face - `((t :inherit default)) - "Face for price in price directive" - :group 'ledger-faces) - -(defface ledger-font-apply-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-apply-account-face - `((t :inherit default)) - "Face for argument of apply account directive" - :group 'ledger-faces) - -(defface ledger-font-apply-tag-face - `((t :inherit default)) - "Face for argument of apply tag directive" - :group 'ledger-faces) - -(defface ledger-font-alias-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-alias-definition-face - `((t :inherit default)) - "Face for aliased account in alias directives" - :group 'ledger-faces) - -(defface ledger-font-assert-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-condition-face - `((t :inherit default)) - "Default face for check and assert conditions" - :group 'ledger-faces) - -(defface ledger-font-assert-condition-face - `((t :inherit ledger-font-condition-face)) - "Face for assert conditions" - :group 'ledger-faces) - -(defface ledger-font-bucket-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-bucket-account-face - `((t :inherit default)) - "Face for bucket directive argument" - :group 'ledger-faces) - -(defface ledger-font-C-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for C directive" - :group 'ledger-faces) - -(defface ledger-font-C-amount-face - `((t :inherit default)) - "Face for amounts in C directives" - :group 'ledger-faces) - -(defface ledger-font-capture-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-capture-account-face - `((t :inherit default)) - "Face for account name in capture directives" - :group 'ledger-faces) - -(defface ledger-font-capture-regex-face - `((t :inherit default)) - "Face for match regex in capture directives" - :group 'ledger-faces) - -(defface ledger-font-check-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-check-condition-face - `((t :inherit ledger-font-condition-face)) - "Face for check conditions" - :group 'ledger-faces) - -(defface ledger-font-commodity-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-commodity-name-face - `((t :inherit font-lock-constant-face)) - "Face for commodity name in commodity directives" - :group 'ledger-faces) - -(defface ledger-font-format-directive-face - `((t :inherit ledger-font-directive-face)) - "Face for format subdirective" - :group 'ledger-faces) - -(defface ledger-font-commodity-format-face - `((t :inherit default)) - "Face for format subdirective argument" - :group 'ledger-faces) - -(defface ledger-font-D-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for D directive" - :group 'ledger-faces) - -(defface ledger-font-define-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-define-name-face - `((t :inherit font-lock-variable-name-face)) - "Face for variable name in define directive" - :group 'ledger-faces) - -(defface ledger-font-define-body-face - `((t :inherit default)) - "Face for body in define directive" - :group 'ledger-faces) - -(defface ledger-font-end-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-expr-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-expr-expression-face - `((t :inherit default)) - "Face for expr and eval expressions" - :group 'ledger-faces) - -(defface ledger-font-fixed-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-fixed-commodity-face - `((t :inherit font-lock-constant-face)) - "Face for commodity name in fixed directive" - :group 'ledger-faces) - -(defface ledger-font-fixed-price-face - `((t :inherit default)) - "Face for price in fixed directive" - :group 'ledger-faces) - -(defface ledger-font-include-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-include-filename-face - `((t :inherit font-lock-string-face)) - "Face for file name in include directives" - :group 'ledger-faces) - -(defface ledger-font-N-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for N directive" - :group 'ledger-faces) - -(defface ledger-font-N-symbol-face - `((t :inherit default)) - "Face for symbol in N directives") - -(defface ledger-font-payee-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-payee-name-face - `((t :inherit font-lock-function-name-face)) - "Face for payee name in payee directive" - :group 'ledger-faces) - -(defface ledger-font-payee-regex-face - `((t :inherit font-lock-string-face)) - "Face for payee subdirective regex in account directive" - :group 'ledger-faces) - -(defface ledger-font-uuid-directive-face - `((t :inherit ledger-font-directive-face)) - "Face for uuid subdirectives" - :group 'ledger-faces) - -(defface ledger-font-uuid-face - `((t :inherit default)) - "Face for uuid in uuid subdirectives" - :group 'ledger-faces) - -(defface ledger-font-tag-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-tag-name-face - `((t :inherit font-lock-type-face)) - "Face for tag name in tag directive" - :group 'ledger-faces) - -(defface ledger-font-timeclock-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for timeclock I,i,O,o,b,h directives" - :group 'ledger-faces) - -(defface ledger-font-year-directive-face - `((t :inherit ledger-font-directive-face)) - "Default face for other transactions" - :group 'ledger-faces) - -(defface ledger-font-year-face - `((t :inherit default)) - "Font for year in year directives" - :group 'ledger-faces) - -(defface ledger-font-posting-account-face - `((t :inherit ledger-font-default-directive-face)) - "Face for Ledger accounts" - :group 'ledger-faces) - -(defface ledger-font-posting-account-cleared-face - `((t :inherit ledger-font-payee-cleared-face)) - "Face for Ledger accounts" - :group 'ledger-faces) - -(defface ledger-font-posting-amount-cleared-face - `((t :inherit ledger-font-posting-account-cleared-face)) - "Face for Ledger accounts" - :group 'ledger-faces) - -(defface ledger-font-posting-account-pending-face - `((t :inherit ledger-font-pending-face)) - "Face for Ledger accounts" - :group 'ledger-faces) - -(defface ledger-font-posting-amount-pending-face - `((t :inherit ledger-font-posting-account-pending-face)) - "Face for Ledger accounts" - :group 'ledger-faces) - -(defface ledger-font-posting-amount-face - `((t :inherit font-lock-constant-face )) - "Face for Ledger amounts" - :group 'ledger-faces) - -(defface ledger-font-posting-date-face - `((t :inherit font-lock-keyword-face)) - "Face for Ledger dates" - :group 'ledger-faces) - -(defface ledger-occur-narrowed-face - `((t :inherit font-lock-comment-face :invisible t)) - "Default face for Ledger occur mode hidden transactions" - :group 'ledger-faces) - -(defface ledger-occur-xact-face - `((t :inherit highlight)) - "Default face for Ledger occur mode shown transactions" - :group 'ledger-faces) - -(defface ledger-font-comment-face - `((t :inherit font-lock-comment-face)) - "Face for Ledger comments" - :group 'ledger-faces) - -(defface ledger-font-reconciler-uncleared-face - `((t :inherit ledger-font-payee-uncleared-face)) - "Default face for uncleared transactions in the reconcile window" - :group 'ledger-faces) - -(defface ledger-font-reconciler-cleared-face - `((t :inherit ledger-font-payee-cleared-face)) - "Default face for cleared (*) transactions in the reconcile window" - :group 'ledger-faces) - -(defface ledger-font-reconciler-pending-face - `((t :inherit ledger-font-pending-face)) - "Default face for pending (!) transactions in the reconcile window" - :group 'ledger-faces) - -(defface ledger-font-report-clickable-face - `((t)) - "Face applied to clickable entries in the report window" - :group 'ledger-faces) - -(defface ledger-font-code-face - `((t :inherit default)) - "Face for Ledger codes" - :group 'ledger-faces) - -(defun ledger-font-face-by-state (num faces) - "Choose one of three faces depending on transaction state. -NUM specifies a match group containing the state. -FACES has the form (CLEARED PENDING OTHER). -Return CLEARED if that group specifies a cleared transaction, -PENDING if pending, and OTHER if none of the above." - (let ((state (save-match-data (ledger-state-from-string (match-string num))))) - (cond ((eq state 'cleared) (nth 0 faces)) - ((eq state 'pending) (nth 1 faces)) - (t (nth 2 faces))))) - -(defun ledger-font-face-by-timeclock-state (num faces) - "Choose one of two faces depending on a timeclock directive character. -NUM specifies a match group containing the character. -FACES has the form (CLEARED UNCLEARED). -Return CLEARED if the character specifies a cleared transaction, -UNCLEARED otherwise." - (if (member (match-string num) '("I" "O")) - (nth 0 faces) - (nth 1 faces))) - -(defun ledger-font-subdirectives (subdirectives) - "Construct anchored highlighters for subdirectives. - -Each element of SUBDIRECTIVES should have the form (MATCHER -SUBEXP-HIGHLIGHTERS…). The result will be a list of elements of -the form (MATCHER PRE-FORM POST-FORM SUBEXP-HIGHLIGHTERS) with -PRE-FORM and POST-FORM set to appropriate values. - -See `font-lock-keywords' for the full description." - - (mapcar (lambda (item) - `(,(car item) - (save-excursion - (save-match-data - (ledger-navigate-end-of-xact)) - (point)) - (goto-char (match-end 0)) - ,@(cdr item))) - subdirectives)) - -(defvar ledger-font-lock-keywords - `(("^[;#%|*].*$" . 'ledger-font-comment-face) - ("^\\(account\\)\\(?:[[:blank:]]\\(.*\\)\\)?$" - (1 'ledger-font-account-directive-face) - (2 'ledger-font-account-name-face nil :lax) - ,@(ledger-font-subdirectives - '(("^[ \t]+\\(;.*\\)" (1 'ledger-font-comment-face)) - ("^[ \t]+\\(note\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-note-directive-face) - (2 'ledger-font-note-text-face nil :lax)) - ("^[ \t]+\\(alias\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-alias-directive-face) - (2 'ledger-font-account-name-face nil :lax)) - ("^[ \t]+\\(payee\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-payee-directive-face) - (2 'ledger-font-payee-regex-face nil :lax)) - ("^[ \t]+\\(check\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-check-directive-face) - (2 'ledger-font-check-condition-face nil :lax)) - ("^[ \t]+\\(assert\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-assert-directive-face) - (2 'ledger-font-assert-condition-face nil :lax)) - ("^[ \t]+\\(eval\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-expr-directive-face) - (2 'ledger-font-expr-expression-face nil :lax)) - ("^[ \t]+\\(default\\)\\>.*" - (1 'ledger-font-default-directive-face))))) - ("^\\(alias\\)\\(?:[[:blank:]]+\\([^=\n]*\\)\\(?:=\\(.*\\)\\)?\\)?$" - (1 'ledger-font-alias-directive-face) - (2 'ledger-font-account-name-face nil :lax) - (3 'ledger-font-alias-definition-face nil :lax)) - (,(concat "^\\(apply\\)\\(?:[[:blank:]]+" - "\\(?:\\(account\\)\\(?:[[:blank:]]+\\(.*\\)\\)?" - "\\|\\(tag\\)\\(?:[[:blank:]]+\\(.*\\)\\)?\\)\\)?$") - (1 'ledger-font-apply-directive-face) - (2 'ledger-font-apply-directive-face nil :lax) - (3 'ledger-font-apply-account-face nil :lax) - (4 'ledger-font-apply-directive-face nil :lax) - (5 'ledger-font-apply-tag-face nil :lax)) - ("^\\(assert\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-assert-directive-face) - (2 'ledger-font-assert-condition-face nil :lax)) - ("^\\(bucket\\|A\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-bucket-directive-face) - (2 'ledger-font-bucket-account-face nil :lax)) - (,(concat "^\\(C\\)" - "\\(?:[[:blank:]]+\\([^=\n]*?\\)[[:blank:]]*" - "\\(?:=[[:blank:]]*\\(.*\\)\\)?\\)?$") - (1 'ledger-font-C-directive-face) - (2 'ledger-font-C-amount-face nil :lax) - (3 'ledger-font-C-amount-face nil :lax)) - (,(concat "^\\(capture\\)" - "\\(?:[[:blank:]]+\\(.*?\\)" - "\\(?:\\(?:\t\\|[ \t]\\{2,\\}\\)\\(.*\\)\\)?\\)?$") - (1 'ledger-font-capture-directive-face) - (2 'ledger-font-capture-account-face nil :lax) - (3 'ledger-font-capture-regex-face nil :lax)) - ("^\\(check\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-check-directive-face) - (2 'ledger-font-check-condition-face nil :lax)) - (,(concat "^\\(?:comment\\|test\\)\\>" - "[^\0]*?\n" - "end[[:blank:]]+\\(?:comment\\|test\\)\\>.*\n") - . 'ledger-font-comment-face) - ("^\\(commodity\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-commodity-directive-face) - (2 'ledger-font-commodity-name-face nil :lax) - ,@(ledger-font-subdirectives - '(("^[ \t]+\\(;.*\\)" (1 'ledger-font-comment-face)) - ("^[ \t]+\\(note\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-note-directive-face) - (2 'ledger-font-note-text-face nil :lax)) - ("^[ \t]+\\(format\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-format-directive-face) - (2 'ledger-font-commodity-format-face nil :lax)) - ("^[ \t]+\\(nomarket\\)\\>.*" - (1 'ledger-font-N-directive-face)) - ("^[ \t]+\\(default\\)\\>.*" - (1 'ledger-font-default-directive-face))))) - ("^\\(D\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-D-directive-face) - (2 'ledger-font-commodity-format-face nil :lax)) - (,(concat "^\\(define\\|def\\)" - "\\(?:[[:blank:]]+\\([^=\n]*?\\)[[:blank:]]*" - "\\(?:=[[:blank:]]*\\(.*\\)\\)?\\)?$") - (1 'ledger-font-define-directive-face) - (2 'ledger-font-define-name-face nil :lax) - (3 'ledger-font-define-body-face nil :lax)) - (,(concat "^\\(end\\)" - "\\(?:[[:blank:]]+\\(apply\\)" - "\\(?:[[:blank:]]+\\(account\\|tag\\)\\>.*\\)?\\)?$") - (1 'ledger-font-end-directive-face) - (2 'ledger-font-end-directive-face nil :lax) - (3 'ledger-font-end-directive-face nil :lax)) - ("^\\(endfixed\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-end-directive-face) - (2 'ledger-font-fixed-commodity-face nil :lax)) - ("^\\(expr\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-expr-directive-face) - (2 'ledger-font-expr-expression-face nil :lax)) - ("^\\(fixed\\)\\(?:[[:blank:]]+\\([^[:blank:]\n]+\\)\\(?:[[:blank:]]+\\(.*\\)\\)?\\)?$" - (1 'ledger-font-fixed-directive-face) - (2 'ledger-font-fixed-commodity-face nil :lax) - (3 'ledger-font-fixed-price-face nil :lax)) - ("^\\(include\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-include-directive-face) - (2 'ledger-font-include-filename-face nil :lax)) - ("^\\(N\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-N-directive-face) - (2 'ledger-font-N-symbol-face nil :lax)) - ("^\\(payee\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-payee-directive-face) - (2 'ledger-font-payee-name-face nil :lax) - ,@(ledger-font-subdirectives - '(("^[ \t]+\\(;.*\\)" (1 'ledger-font-comment-face)) - ("^[ \t]+\\(alias\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-alias-directive-face) - (2 'ledger-font-payee-regex-face nil :lax)) - ("^[ \t]+\\(uuid\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-uuid-directive-face) - (2 'ledger-font-uuid-face nil :lax))))) - (,(concat "^\\(P\\)" - "\\(?:[[:blank:]]+\\([^[:blank:]\n]+" - "\\(?:[[:blank:]]+[[:digit:]][^[:blank:]\n]*\\)?\\)" - "\\(?:[[:blank:]]+\\(\".*?\"\\|[^[:blank:]\n]+\\)" - "\\(?:[[:blank:]]+\\(.*\\)\\)?\\)?\\)?$") - (1 'ledger-font-price-directive-face) - (2 'ledger-font-price-date-face nil :lax) - (3 'ledger-font-price-symbol-face nil :lax) - (4 'ledger-font-price-face nil :lax)) - ("^\\(tag\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-tag-directive-face) - (2 'ledger-font-tag-name-face nil :lax) - ,@(ledger-font-subdirectives - '(("^[ \t]+\\(;.*\\)" (1 'ledger-font-comment-face)) - ("^[ \t]+\\(check\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-check-directive-face) - (2 'ledger-font-check-condition-face nil :lax)) - ("^[ \t]+\\(assert\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-assert-directive-face) - (2 'ledger-font-assert-condition-face nil :lax))))) - (,(concat "^\\([IiOo]\\)" - "\\(?:[[:blank:]]+\\([^[:blank:]\n]+" - "\\(?:[[:blank:]]+[^[:blank:]\n]+\\)?\\)" - "\\(?:[[:blank:]]+\\(.*?\\)" - "\\(?:\\(?:\t\\|[ \t]\\{2,\\}\\)\\(.*?\\)" - "\\(?:\\(?:\t\\|[ \t]\\{2,\\}\\)\\(;.*\\)\\)?\\)?\\)?\\)?$") - (1 'ledger-font-timeclock-directive-face) - (2 'ledger-font-posting-date-face nil :lax) - (3 (ledger-font-face-by-timeclock-state 1 '(ledger-font-posting-account-cleared-face - ledger-font-posting-account-face)) nil :lax) - (4 (ledger-font-face-by-timeclock-state 1 '(ledger-font-payee-cleared-face - ledger-font-payee-uncleared-face)) nil :lax) - (5 'ledger-font-comment-face nil :lax)) - ("^\\([bh]\\)\\>.*$" (1 'ledger-font-timeclock-directive-face)) - ("^\\(year\\|Y\\)\\(?:[[:blank:]]+\\(.*\\)\\)?$" - (1 'ledger-font-year-directive-face) - (2 'ledger-font-year-face nil :lax)) - - (,(lambda (limit) - (when ledger-fontify-xact-state-overrides - (re-search-forward - (concat "^\\(?:\\([=~]\\)[ \t].*\\|" ; auto/periodic, subexpr 1 - "[[:digit:]][^ \t\n]*" ; date - "[ \t]+\\([*!]\\)" ; mark, subexp 2 - ".*\\)" ; rest of header - "\\(?:\n[ \t]+.*\\)*" ; postings - ) - limit t))) - (0 (cond ((equal "=" (match-string 1)) 'ledger-font-auto-xact-face) - ((equal "~" (match-string 1)) 'ledger-font-periodic-xact-face) - (t (ledger-font-face-by-state 2 '(ledger-font-xact-cleared-face - ledger-font-xact-pending-face)))))) - (,(concat "^\\(?:\\(\\([=~]\\).*\\)\\|" ; auto/periodic, subexp 1, 2 - "\\([[:digit:]][^ \t\n]*\\)" ; date, subexp 3 - ledger-xact-after-date-regex "\\)") ; mark 4, code 5, desc 6, comment 7 - (1 (cond ((equal "=" (match-string 2)) 'ledger-font-auto-xact-face) - ((equal "~" (match-string 2)) 'ledger-font-periodic-xact-face) - (t 'ledger-font-default-directive-face)) - nil :lax) - (3 'ledger-font-posting-date-face nil :lax) - (5 'ledger-font-code-face nil :lax) - (6 (ledger-font-face-by-state 4 '(ledger-font-payee-cleared-face - ledger-font-payee-pending-face - ledger-font-payee-uncleared-face)) - nil :lax) - (7 'ledger-font-comment-face nil :lax) - ,@(ledger-font-subdirectives - `(("^[ \t]+\\(;.*\\)" - (1 'ledger-font-comment-face)) - (,ledger-posting-regex ; state and account 1, state 2, account 3, amount 4, comment 5 - (1 (ledger-font-face-by-state 2 '(ledger-font-posting-account-cleared-face - ledger-font-posting-account-pending-face - ledger-font-posting-account-face)) - nil :lax) - (4 (ledger-font-face-by-state 2 '(ledger-font-posting-amount-cleared-face - ledger-font-posting-amount-pending-face - ledger-font-posting-amount-face)) - nil :lax) - (5 'ledger-font-comment-face nil :lax)))))) - "Expressions to highlight in Ledger mode.") - - - -(provide 'ledger-fonts) - -;;; ledger-fonts.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-fonts.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-fonts.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-init.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-init.el @@ -1,97 +0,0 @@ -;;; ledger-init.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - -;;; Commentary: -;; Determine the ledger environment - -(require 'ledger-regex) - -;;; Code: - -(defcustom ledger-init-file-name "~/.ledgerrc" - "Location of the ledger initialization file. nil if you don't have one." - :type '(choice (const :tag "Do not read ledger initialization file" nil) - file) - :group 'ledger-exec) - -(defvar ledger-environment-alist nil - "Variable to hold details about ledger-mode's environment. - -Adding the dotted pair (\"decimal-comma\" . t) will tell ledger -to treat commas as decimal separator. - -This variable is automatically populated by -`ledger-init-load-init-file', which is called in the body of -`ledger-mode'.") - -(defconst ledger-iso-date-format "%Y-%m-%d" - "The format for ISO 8601 dates.") - -(defcustom ledger-default-date-format "%Y/%m/%d" - "The date format that ledger uses throughout. -Set this to the value of `ledger-iso-date-format' if you prefer -ISO 8601 dates." - :type 'string - :package-version '(ledger-mode . "4.0.0") - :group 'ledger) - -(defun ledger-format-date (&optional date) - "Format DATE according to the current preferred date format. -Returns the current date if DATE is nil or not supplied." - (format-time-string - (or (cdr (assoc "input-date-format" ledger-environment-alist)) - ledger-default-date-format) - date)) - - -(defun ledger-init-parse-initialization (buffer) - "Parse the .ledgerrc file in BUFFER." - (with-current-buffer buffer - (let (environment-alist) - (goto-char (point-min)) - (while (re-search-forward ledger-init-string-regex nil t) - (let ((matchb (match-beginning 0)) ;; save the match data, string-match stamp on it - (matche (match-end 0))) - (end-of-line) - (push (cons (let ((flag (buffer-substring-no-properties (+ 2 matchb) matche))) - (if (string-match "[ \t\n\r]+\\'" flag) - (replace-match "" t t flag) - flag)) - (let ((value (buffer-substring-no-properties matche (point)))) - (if (> (length value) 0) - value - t))) - environment-alist))) - (nreverse environment-alist)))) - -(defun ledger-init-load-init-file () - "Load and parse the .ledgerrc file into `ledger-environment-alist'." - (interactive) - (when (and ledger-init-file-name - (file-readable-p ledger-init-file-name)) - (with-temp-buffer - (insert-file-contents ledger-init-file-name) - (setq ledger-environment-alist - (ledger-init-parse-initialization (current-buffer)))))) - -(provide 'ledger-init) - -;;; ledger-init.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-init.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-init.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-mode-autoloads.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-mode-autoloads.el @@ -1,140 +0,0 @@ -;;; ledger-mode-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 ledger-check.el - -(register-definition-prefixes "ledger-check" '("ledger-")) - - -;;; Generated autoloads from ledger-commodities.el - -(register-definition-prefixes "ledger-commodities" '("ledger-")) - - -;;; Generated autoloads from ledger-complete.el - -(register-definition-prefixes "ledger-complete" '("ledger-")) - - -;;; Generated autoloads from ledger-context.el - -(register-definition-prefixes "ledger-context" '("ledger-")) - - -;;; Generated autoloads from ledger-exec.el - -(register-definition-prefixes "ledger-exec" '("ledger-")) - - -;;; Generated autoloads from ledger-flymake.el - -(autoload 'ledger-flymake-enable "ledger-flymake" "\ -Enable `flymake-mode' in `ledger-mode' buffers.") -(register-definition-prefixes "ledger-flymake" '("ledger-")) - - -;;; Generated autoloads from ledger-fontify.el - -(register-definition-prefixes "ledger-fontify" '("ledger-fontify-")) - - -;;; Generated autoloads from ledger-fonts.el - -(register-definition-prefixes "ledger-fonts" '("ledger-font-")) - - -;;; Generated autoloads from ledger-init.el - -(register-definition-prefixes "ledger-init" '("ledger-")) - - -;;; Generated autoloads from ledger-mode.el - -(autoload 'ledger-mode "ledger-mode" "\ -A mode for editing ledger data files. - -(fn)" t) -(add-to-list 'auto-mode-alist '("\\.ledger\\'" . ledger-mode)) -(register-definition-prefixes "ledger-mode" '("ledger-")) - - -;;; Generated autoloads from ledger-navigate.el - -(register-definition-prefixes "ledger-navigate" '("ledger-navigate-")) - - -;;; Generated autoloads from ledger-occur.el - -(register-definition-prefixes "ledger-occur" '("ledger-occur")) - - -;;; Generated autoloads from ledger-post.el - -(register-definition-prefixes "ledger-post" '("ledger-")) - - -;;; Generated autoloads from ledger-reconcile.el - -(register-definition-prefixes "ledger-reconcile" '("ledger-")) - - -;;; Generated autoloads from ledger-regex.el - -(register-definition-prefixes "ledger-regex" '("ledger-")) - - -;;; Generated autoloads from ledger-report.el - -(register-definition-prefixes "ledger-report" '("ledger-")) - - -;;; Generated autoloads from ledger-schedule.el - -(register-definition-prefixes "ledger-schedule" '("ledger-")) - - -;;; Generated autoloads from ledger-sort.el - -(register-definition-prefixes "ledger-sort" '("ledger-sort-")) - - -;;; Generated autoloads from ledger-state.el - -(register-definition-prefixes "ledger-state" '("ledger-")) - - -;;; Generated autoloads from ledger-test.el - -(register-definition-prefixes "ledger-test" '("ledger-")) - - -;;; Generated autoloads from ledger-texi.el - -(register-definition-prefixes "ledger-texi" '("ledger-")) - - -;;; Generated autoloads from ledger-xact.el - -(register-definition-prefixes "ledger-xact" '("ledger-")) - -;;; End of scraped data - -(provide 'ledger-mode-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; ledger-mode-autoloads.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-mode-pkg.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-mode-pkg.el @@ -1,6 +0,0 @@ -(define-package "ledger-mode" "20240326.1834" "Helper code for use with the \"ledger\" command-line tool" - '((emacs "25.1")) - :commit "e6946feced023c223650ac31cbe5c96bea2a7afb") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-mode.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-mode.el @@ -1,339 +0,0 @@ -;;; ledger-mode.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; Package-Requires: ((emacs "25.1")) - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - -;;; Commentary: -;; This Emacs library provides a major mode for editing files in the format used -;; by the `ledger' command-line accounting system. - -;; It also provides automated support for some `ledger' workflows, such as -;; reconciling transactions, or running certain reports. - -;;; Code: - -(require 'ledger-regex) -(require 'org) -(require 'ledger-commodities) -(require 'ledger-complete) -(require 'ledger-context) -(require 'ledger-exec) -(require 'ledger-fonts) -(require 'ledger-fontify) -(require 'ledger-init) -(require 'ledger-navigate) -(require 'ledger-occur) -(require 'ledger-post) -(require 'ledger-reconcile) -(require 'ledger-report) -(require 'ledger-sort) -(require 'ledger-state) -(require 'ledger-test) -(require 'ledger-texi) -(require 'ledger-xact) -(require 'ledger-schedule) -(require 'ledger-check) - -(declare-function custom-group-members "cus-edit" (symbol groups-only)) - -;;; Code: - -(defgroup ledger nil - "Interface to the Ledger command-line accounting program." - :group 'data) - -(defconst ledger-version "3.0" - "The version of ledger.el currently loaded.") - -(defconst ledger-mode-version "4.0.0") - -(defun ledger-mode-dump-variable (var) - "Format VAR for dump to buffer." - (if var - (insert (format " %s: %S\n" (symbol-name var) (eval var))))) - -(defun ledger-mode-dump-group (group) - "Dump GROUP customizations to current buffer." - (require 'cus-edit) - (let ((members (custom-group-members group nil))) - (dolist (member members) - (cond ((eq (cadr member) 'custom-group) - (insert (format "Group %s:\n" (symbol-name (car member)))) - (ledger-mode-dump-group (car member))) - ((eq (cadr member) 'custom-variable) - (ledger-mode-dump-variable (car member))))))) - -(defun ledger-mode-dump-configuration () - "Dump all customizations." - (interactive) - (find-file "ledger-mode-dump") - (ledger-mode-dump-group 'ledger)) - -(defun ledger-read-account-with-prompt (prompt) - "Read an account from the minibuffer with PROMPT." - (let* ((context (ledger-context-at-point)) - (account (ledger-context-field-value context 'account))) - (ledger-completing-read-with-default prompt - (when account - (regexp-quote account)) - (ledger-accounts-list)))) - -(defun ledger-read-date (prompt) - "Return user-supplied date after `PROMPT', defaults to today. -This uses `org-read-date', which see." - (ledger-format-date (let ((org-read-date-prefer-future nil)) - (org-read-date nil t nil prompt)))) - -(defun ledger-get-minibuffer-prompt (prompt default) - "Return a minibuffer prompt string composing PROMPT and DEFAULT." - (concat prompt - (if default - (concat " (" default "): ") - ": "))) - -(defun ledger-completing-read-with-default (prompt default collection) - "Return a user-supplied string after PROMPT. -Use the given DEFAULT, while providing completions from COLLECTION." - (completing-read (ledger-get-minibuffer-prompt prompt default) - collection nil nil nil 'ledger-minibuffer-history default)) - -(defun ledger-read-string-with-default (prompt default) - "Return user supplied string after PROMPT, or DEFAULT." - (read-string (ledger-get-minibuffer-prompt prompt default) - nil 'ledger-minibuffer-history default)) - -(defun ledger-display-balance-at-point (&optional arg) - "Display the cleared-or-pending balance. -And calculate the target-delta of the account being reconciled. - -With ARG (\\[universal-argument]) ask for the target commodity and convert -the balance into that." - (interactive "P") - (let* ((account (ledger-read-account-with-prompt "Account balance to show")) - (target-commodity (when arg (ledger-read-commodity-with-prompt "Target commodity: "))) - (buffer (find-file-noselect (ledger-master-file))) - (balance (with-temp-buffer - (apply 'ledger-exec-ledger buffer (current-buffer) "cleared" account - (when target-commodity (list "-X" target-commodity))) - (if (> (buffer-size) 0) - (buffer-substring-no-properties (point-min) (1- (point-max))) - (concat account " is empty."))))) - (when balance - (display-message-or-buffer balance)))) - -(defun ledger-display-ledger-stats () - "Display some summary statistics about the current ledger file." - (interactive) - (let* ((buffer (find-file-noselect (ledger-master-file))) - (balance (with-temp-buffer - (ledger-exec-ledger buffer (current-buffer) "stats") - (buffer-substring-no-properties (point-min) (1- (point-max)))))) - (when balance - (message balance)))) - -(defvar ledger-mode-abbrev-table) - -(defvar ledger-date-string-today (ledger-format-date)) - -(defun ledger-remove-effective-date () - "Remove the effective date from a transaction or posting." - (interactive) - (let ((context (car (ledger-context-at-point)))) - (save-excursion - (save-restriction - (narrow-to-region (line-beginning-position) (line-end-position)) - (beginning-of-line) - (cond ((eq 'xact context) - (re-search-forward ledger-iso-date-regexp) - (when (= (char-after) ?=) - (let ((eq-pos (point))) - (delete-region - eq-pos - (re-search-forward ledger-iso-date-regexp))))) - ((eq 'acct-transaction context) - ;; Match "; [=date]" & delete string - (when (re-search-forward - (concat ledger-comment-regex - "\\[=" ledger-iso-date-regexp "\\]") - nil 'noerr) - (replace-match "")))))))) - -(defun ledger-insert-effective-date (&optional date) - "Insert effective date `DATE' to the transaction or posting. - -If `DATE' is nil, prompt the user a date. - -Replace the current effective date if there's one in the same -line. - -With a prefix argument, remove the effective date." - (interactive) - (if (and (listp current-prefix-arg) - (= 4 (prefix-numeric-value current-prefix-arg))) - (ledger-remove-effective-date) - (let* ((context (car (ledger-context-at-point))) - (date-string (or date (ledger-read-date "Effective date: ")))) - (save-restriction - (narrow-to-region (line-beginning-position) (line-end-position)) - (cond - ((eq 'xact context) - (beginning-of-line) - (re-search-forward ledger-iso-date-regexp) - (when (= (char-after) ?=) - (ledger-remove-effective-date)) - (insert "=" date-string)) - ((eq 'acct-transaction context) - (end-of-line) - (ledger-remove-effective-date) - (insert " ; [=" date-string "]"))))))) - -(defun ledger-mode-remove-extra-lines () - "Get rid of multiple empty lines." - (goto-char (point-min)) - (while (re-search-forward "\n\n\\(\n\\)+" nil t) - (replace-match "\n\n"))) - -(defun ledger-mode-clean-buffer () - "Indent, remove multiple line feeds and sort the buffer." - (interactive) - (let ((start (point-min-marker)) - (end (point-max-marker)) - (distance-in-xact (- (point) (ledger-navigate-beginning-of-xact)))) - (let ((target (buffer-substring (line-beginning-position) (line-end-position)))) - (goto-char start) - (untabify start end) - (ledger-sort-buffer) - (ledger-post-align-postings start end) - (ledger-mode-remove-extra-lines) - (goto-char start) - (search-forward target) - (beginning-of-line) - (forward-char distance-in-xact)))) - -(defvar ledger-mode-syntax-table - (let ((table (make-syntax-table text-mode-syntax-table))) - (modify-syntax-entry ?\; "<" table) - (modify-syntax-entry ?\n ">" table) - table) - "Syntax table in use in `ledger-mode' buffers.") - -(defvar ledger-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-a") #'ledger-add-transaction) - (define-key map (kbd "C-c C-b") #'ledger-post-edit-amount) - (define-key map (kbd "C-c C-c") #'ledger-toggle-current) - (define-key map (kbd "C-c C-d") #'ledger-delete-current-transaction) - (define-key map (kbd "C-c C-e") #'ledger-toggle-current-transaction) - (define-key map (kbd "C-c C-f") #'ledger-occur) - (define-key map (kbd "C-c C-k") #'ledger-copy-transaction-at-point) - (define-key map (kbd "C-c C-r") #'ledger-reconcile) - (define-key map (kbd "C-c C-s") #'ledger-sort-region) - (define-key map (kbd "C-c C-t") #'ledger-insert-effective-date) - (define-key map (kbd "C-c C-u") #'ledger-schedule-upcoming) - (define-key map (kbd "C-c C-p") #'ledger-display-balance-at-point) - (define-key map (kbd "C-c C-l") #'ledger-display-ledger-stats) - (define-key map (kbd "C-c C-q") #'ledger-post-align-xact) - - (define-key map (kbd "C-TAB") #'ledger-post-align-xact) - (define-key map (kbd "C-c TAB") #'ledger-fully-complete-xact) - (define-key map (kbd "C-c C-i") #'ledger-fully-complete-xact) - - (define-key map (kbd "C-c C-o C-a") #'ledger-report-redo) - (define-key map (kbd "C-c C-o C-e") #'ledger-report-edit-report) - (define-key map (kbd "C-c C-o C-g") #'ledger-report-goto) - (define-key map (kbd "C-c C-o C-k") #'ledger-report-quit) - (define-key map (kbd "C-c C-o C-r") #'ledger-report) - (define-key map (kbd "C-c C-o C-s") #'ledger-report-save) - - (define-key map (kbd "M-p") #'ledger-navigate-prev-xact-or-directive) - (define-key map (kbd "M-n") #'ledger-navigate-next-xact-or-directive) - (define-key map (kbd "M-q") #'ledger-post-align-dwim) - - ;; Reset the `text-mode' override of this standard binding - (define-key map (kbd "C-M-i") 'completion-at-point) - map) - "Keymap for `ledger-mode'.") - -(easy-menu-define ledger-mode-menu ledger-mode-map - "Ledger menu" - '("Ledger" - ["Narrow to REGEX" ledger-occur] - ["Show all transactions" ledger-occur-mode ledger-occur-mode] - ["Ledger Statistics" ledger-display-ledger-stats ledger-works] - "---" - ["Show upcoming transactions" ledger-schedule-upcoming] - ["Add Transaction (ledger xact)" ledger-add-transaction ledger-works] - ["Complete Transaction" ledger-fully-complete-xact] - ["Delete Transaction" ledger-delete-current-transaction] - "---" - ["Calc on Amount" ledger-post-edit-amount] - "---" - ["Check Balance" ledger-display-balance-at-point ledger-works] - ["Reconcile Account" ledger-reconcile ledger-works] - "---" - ["Toggle Current Transaction" ledger-toggle-current-transaction] - ["Toggle Current Posting" ledger-toggle-current] - ["Copy Trans at Point" ledger-copy-transaction-at-point] - "---" - ["Clean-up Buffer" ledger-mode-clean-buffer] - ["Check Buffer" ledger-check-buffer ledger-works] - ["Align Region" ledger-post-align-postings mark-active] - ["Align Xact" ledger-post-align-xact] - ["Sort Region" ledger-sort-region mark-active] - ["Sort Buffer" ledger-sort-buffer] - ["Mark Sort Beginning" ledger-sort-insert-start-mark] - ["Mark Sort End" ledger-sort-insert-end-mark] - ["Set effective date" ledger-insert-effective-date] - "---" - ["Customize Ledger Mode" (lambda () (interactive) (customize-group 'ledger))] - "---" - ["Run Report" ledger-report ledger-works] - ["Goto Report" ledger-report-goto ledger-works] - ["Re-run Report" ledger-report-redo ledger-works] - ["Save Report" ledger-report-save ledger-works] - ["Edit Report" ledger-report-edit-report ledger-works] - ["Quit Report" ledger-report-quit ledger-works])) - -;;;###autoload -(define-derived-mode ledger-mode text-mode "Ledger" - "A mode for editing ledger data files." - (ledger-check-version) - (setq font-lock-defaults - '(ledger-font-lock-keywords t nil nil nil)) - (add-hook 'font-lock-extend-region-functions 'ledger-fontify-extend-region) - (add-hook 'completion-at-point-functions #'ledger-complete-at-point nil t) - (add-hook 'after-save-hook 'ledger-report-redo nil t) - - (add-hook 'post-command-hook 'ledger-highlight-xact-under-point nil t) - - (ledger-init-load-init-file) - (setq-local comment-start ";") - (setq-local indent-line-function #'ledger-indent-line) - (setq-local indent-region-function 'ledger-post-align-postings) - (setq-local beginning-of-defun-function #'ledger-navigate-beginning-of-xact) - (setq-local end-of-defun-function #'ledger-navigate-end-of-xact)) - -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.ledger\\'" . ledger-mode)) - -(provide 'ledger-mode) - -;;; ledger-mode.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-mode.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-mode.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-mode.info b/emacs/elpa/ledger-mode-20240326.1834/ledger-mode.info @@ -1,1554 +0,0 @@ -This is ledger-mode.info, produced by makeinfo version 6.7 from -ledger-mode.texi. - -Copyright © 2013, Craig Earls. All rights reserved. - - Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - • Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - • Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the - distribution. - - • Neither the name of New Artisans LLC nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -“AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -INFO-DIR-SECTION Emacs -START-INFO-DIR-ENTRY -* Ledger Mode: (ledger-mode). Command-Line Accounting -END-INFO-DIR-ENTRY - - -File: ledger-mode.info, Node: Top, Next: Introduction to Ledger-mode, Prev: (dir), Up: (dir) - -Overview -******** - -Ledger is a command line accounting tool that provides double-entry -accounting based on a text journal. It provides no bells or whistles, -and returns the user to the days before user interfaces were even a -twinkling in their father’s CRT. - - Ledger-mode assists you in maintaining input files for Ledger, -running reports and much more... - -* Menu: - -* Introduction to Ledger-mode:: -* The Ledger Buffer:: -* The Reconcile Buffer:: -* The Report Buffer:: -* Scheduling Transactions:: -* Customizing Ledger-mode:: -* Generating Ledger Regression Tests:: -* Embedding Example results in Ledger Documentation:: -* Hacking Ledger-mode:: -* Concept Index:: -* Command & Variable Index:: -* Keystroke Index:: - - -File: ledger-mode.info, Node: Introduction to Ledger-mode, Next: The Ledger Buffer, Prev: Top, Up: Top - -1 Introduction to Ledger-mode -***************************** - -* Menu: - -* Quick Installation:: -* Menus:: -* Quick Demo:: - - -File: ledger-mode.info, Node: Quick Installation, Next: Menus, Prev: Introduction to Ledger-mode, Up: Introduction to Ledger-mode - -1.1 Quick Installation -====================== - -The Emacs lisp source for Ledger-mode is included with the source -distribution of Ledger. It is entirely included in the ‘lisp’ -subdirectory. To use Ledger-mode, include the following in your Emacs -initialization file (‘~/.emacs’, ‘~/.emacs.d/init.el’, or -‘~/.Aquamacs/Preferences.el’). - - (autoload 'ledger-mode "ledger-mode" "A major mode for Ledger" t) - (add-to-list 'load-path - (expand-file-name "/path/to/ledger/source/lisp/")) - (add-to-list 'auto-mode-alist '("\\.ledger$" . ledger-mode)) - - This sets up Emacs to automatically recognize files that end with -‘.ledger’ and start Ledger-mode. Nothing else should be required as -long as the ledger command line utility is properly installed. - - -File: ledger-mode.info, Node: Menus, Next: Quick Demo, Prev: Quick Installation, Up: Introduction to Ledger-mode - -1.2 Menus -========= - -The vast majority of Ledger-mode functionality is available from the -Emacs menu system. The keystrokes are shown in the menu to help you -learn the faster keyboard methods. - - -File: ledger-mode.info, Node: Quick Demo, Prev: Menus, Up: Introduction to Ledger-mode - -1.3 Quick Demo -============== - -Load the demo file ‘demo.ledger’ from the Ledger source ‘test/input’ -directory. The ledger will be loaded and font highlighted. At this -point you could manually edit transactions and run Ledger from a -convenient command line. - -* Menu: - -* Quick Add:: -* Reconciliation:: -* Reports:: -* Narrowing:: - - -File: ledger-mode.info, Node: Quick Add, Next: Reconciliation, Prev: Quick Demo, Up: Quick Demo - -1.3.1 Quick Add ---------------- - -As simple as the Ledger transaction format is, it can still be daunting -to add many transactions manually. Ledger provides two ways to add -transactions with minimal typing. Both are based on the idea that most -transactions are repetitions of earlier transactions. - - In the ‘demo.ledger’ buffer enter a date using the correct format. -Then type the first few characters of another payee in the ‘demo.ledger’ -buffer. Type ‘C-c TAB’. Ledger-mode will search for a Payee that has -the same beginning and copy the rest of the transaction to you new -entry. - - Additionally you can use the ledger ‘xact’ command, by either typing -‘C-c C-a’ or using ‘Add Transaction’ menu entry. Then typing a close -match to the payee. Ledger-mode will call ‘ledger xact’ with the data -you enter and place the transaction in the proper chronological place in -the ledger. The date format can be changed by modifying -‘ledger-default-date-format’. - - -File: ledger-mode.info, Node: Reconciliation, Next: Reports, Prev: Quick Add, Up: Quick Demo - -1.3.2 Reconciliation --------------------- - -The biggest task of maintaining a ledger is ensuring that it matches the -outside world. This process is called reconciliation (*note Basics of -Reconciliation::) and can be quite onerous. Ledger-mode attempts to -make it as painless as possible. - - In the ‘demo.ledger’ buffer type ‘C-c C-r’. If cursor is on an -account, Ledger-mode will propose this account, or in the Minibuffer, -will prompt for an account to reconcile. Hit ‘RET’ if you are happy -with proposed account, or enter ‘Checking’ as example. Emacs will then -prompt for a target value. The target value is the amount you want the -cleared transactions in the buffer to total. Normally this would be the -ending value from your bank statement, or the latest value in your -on-line transaction summary. Enter ‘1710’. Note that Ledger-mode -assumes you are using ‘$’ (USD) as your default commodity, this can be -easily changed in the customization variables. *Note Ledger-mode -Customization::. - - You now see a list of uncleared transactions in a buffer below the -‘demo.ledger’ buffer. Touching the ‘SPC’ bar will mark a transaction as -pending and display the current cleared (and pending) balance, along -with the difference remaining to meet your target. Clear the first -three transactions, and you will see the difference to target reach -‘$0’. End the reconciliation by typing ‘C-c C-c’. This saves the -‘demo.ledger’ buffer and marks the transactions and finally cleared. -Type ‘q’ to close out the reconciliation buffer. - - -File: ledger-mode.info, Node: Reports, Next: Narrowing, Prev: Reconciliation, Up: Quick Demo - -1.3.3 Reports -------------- - -The real power of Ledger is in its reporting capabilities. Reports can -be run and displayed in a separate Emacs buffer. In the ‘demo.ledger’ -buffer, type ‘C-c C-o C-r’. In the Minibuffer Emacs will prompt for a -report name. There are a few built-in reports, and you can add any -report you need *Note Adding and Editing Reports::. - - In the Minibuffer type ‘account’. When prompted for an account type -‘checking’. In a buffer named ‘*Ledger Report*’, you will see a Ledger -register report. You can move around the buffer, with the point on a -transaction, type ‘RET’. Ledger-mode will take you directly to that -transaction in the ‘demo.ledger’ buffer. - - Another built-in report is the balance report. In the ‘demo.ledger’ -buffer, type ‘C-c C-o C-r’. When prompted for a report to run, type -‘bal’, and a balance report of all accounts will be shown. - - -File: ledger-mode.info, Node: Narrowing, Prev: Reports, Up: Quick Demo - -1.3.4 Narrowing ---------------- - -A ledger file can get very large. It can be helpful to collapse the -buffer to display only the transactions you are interested in. -Ledger-mode copies the ‘occur’ mode functionality. Typing ‘C-c C-f’ and -entering any regex in the Minibuffer will show only transactions that -match the regex. The regex can be on any field, or amount. Use ‘C-c -C-g’ after editing transactions to re-apply the current regex. Cancel -the narrowing by typing ‘C-c C-f’ again. - - -File: ledger-mode.info, Node: The Ledger Buffer, Next: The Reconcile Buffer, Prev: Introduction to Ledger-mode, Up: Top - -2 The Ledger Buffer -******************* - -* Menu: - -* Navigating Transactions:: -* Adding Transactions:: -* Copying Transactions:: -* Editing Amounts:: -* Marking Transactions:: -* Formatting Transactions:: -* Deleting Transactions:: -* Sorting Transactions:: -* Narrowing Transactions:: - - -File: ledger-mode.info, Node: Navigating Transactions, Next: Adding Transactions, Prev: The Ledger Buffer, Up: The Ledger Buffer - -2.1 Navigating Transactions -=========================== - -In addition to the usual Emacs navigation commands, ledger-mode offers -several additional commands to ease navigation. ‘M-n’ and ‘M-p’ -navigate between next and previous xacts or directives. - - Additionally, ‘M-x ledger-navigate-next-uncleared’ and ‘M-x -ledger-navigate-previous-uncleared’ navigate to the next and previous -uncleared transactions. - - -File: ledger-mode.info, Node: Adding Transactions, Next: Copying Transactions, Prev: Navigating Transactions, Up: The Ledger Buffer - -2.2 Adding Transactions -======================= - -Beyond the two ways of quickly adding transactions (*note Quick Add::) -Ledger-mode assists you by providing robust ‘TAB’ completion for payees -and accounts. Ledger-mode will scan the existing buffer for payees and -accounts. Included files are not currently included in the completion -scan. Ledger-mode respects Emacs’s variables that govern ‘TAB’ -completion, see especially ‘tab-always-indent’. - - To cycle between completions when hitting ‘TAB’ multiple times, you -can adjust the standard completion configuration like this: - - (add-hook 'ledger-mode-hook - (lambda () - (setq-local tab-always-indent 'complete) - (setq-local completion-cycle-threshold t) - (setq-local ledger-complete-in-steps t))) - - Ledger-mode will help you keep your amounts aligned. When indenting -or completing, Ledger-mode will automatically place any amounts such -that their last digit is aligned to the column specified by -‘ledger-post-amount-alignment-column’, which defaults to ‘52’. *Note -Ledger Post Customization Group::. - - To prevent the automatic realignment of amounts, disable -‘ledger-post-auto-align’. *Note Ledger Post Customization Group::. - -* Menu: - -* Setting a Transactions Effective Date:: -* Quick Balance Display:: - - -File: ledger-mode.info, Node: Setting a Transactions Effective Date, Next: Quick Balance Display, Prev: Adding Transactions, Up: Adding Transactions - -2.2.1 Setting a Transactions Effective Date -------------------------------------------- - -Ledger provides for adding information to a transaction that add details -to the dates. For example, you can specify when the transaction was -entered, when the transaction was cleared, or when individual postings -were cleared. Ledger-mode refers to these additional dates as -_effective_ dates. To set the effective date of a transaction, place -the point in the first line of a transaction and type ‘C-c C-t’. The -effective date will be added to the transaction. To set the effective -date for an individual posting, place point in the posting and type ‘C-c -C-t’ and the effective date for that posting will be added at the end of -the posting. - - -File: ledger-mode.info, Node: Quick Balance Display, Prev: Setting a Transactions Effective Date, Up: Adding Transactions - -2.2.2 Quick Balance Display ---------------------------- - -You will often want to quickly check the balance of an account. The -easiest way is to position point on the account you are interested in, -and type ‘C-c C-p’. The Minibuffer will ask you to verify the name of -the account you want, if it is already correct hit ‘RET’, then the -balance of the account will be displayed in the Minibuffer. - - -File: ledger-mode.info, Node: Copying Transactions, Next: Editing Amounts, Prev: Adding Transactions, Up: The Ledger Buffer - -2.3 Copying Transactions -======================== - -An easy way to copy a transaction is to type ‘C-c C-k’ or menu entry -‘Copy Trans at Point’. You will be prompted the new date for the copied -transaction, and after having confirmed with ‘RET’, new transaction will -be inserted at _date_ position in buffer. - - If you prefer to keep blank lines between your transactions, you can -change the default in ‘ledger-copy-transaction-insert-blank-line-after’. - - -File: ledger-mode.info, Node: Editing Amounts, Next: Marking Transactions, Prev: Copying Transactions, Up: The Ledger Buffer - -2.4 Editing Amounts -=================== - -GNU Emacs Calculator, aka ‘Calc’, is a very powerful Reverse Polish -Notation calculator built into all recent version of Emacs. Ledger-mode -makes it easy to calculate values for amount by integrating ‘Calc’. -With the point anywhere in the same line as a posting, typing ‘C-c C-b’ -will bring up the ‘Calc’ buffer, and push the current amount for the -posting onto the top of the ‘Calc’ stack. Perform any calculations you -need to arrive at the final value, then type ‘y’ to yank the value at -the top of stack back into the ledger buffer. Note: ‘Calc’ does not -directly support commas as decimal separators. Ledger-mode will -translate values from decimal-comma format to decimal-period format for -use in ‘Calc’, but it cannot intercept the value being yanked from the -‘Calc’ stack, so decimal-comma users will have to manually replace the -period with a comma. - - -File: ledger-mode.info, Node: Marking Transactions, Next: Formatting Transactions, Prev: Editing Amounts, Up: The Ledger Buffer - -2.5 Marking Transactions -======================== - -Ledger considers transaction or posting to be in one of three states: -uncleared, cleared, and pending. For calculation Ledger ignores these -states unless specifically instructed to use them. Ledger-mode assigns -some additional meaning to the states: - - • Uncleared. No state. This is equivalent to sticking a check in - the mail. It has been obligated, but not been cashed by the - recipient. It could also apply to credit/debit card transactions - that have not been cleared into your account balance. You bank may - call these transactions _pending_, but Ledger-mode uses a slightly - different meaning. - - • Pending. Ledger-mode’s reconciliation function see pending - transactions as an intermediate step in reconciling an account. - When doing a reconciliation (*note Reconciliation::), marking a - transaction as pending means that you have seen the transaction - finally recorded by the recipient, but you have not completely - reconciled the account. - - • Cleared. The transaction has been completely recognized by all - parties to the transaction. - - Typing ‘C-c C-c’, depending where is the point, will clear the -complete transaction, or an individual posting. This places an asterisk -‘*’ prior to the payee for the complete transaction, or prior to the -account for an individual posting. When point is inside a transaction, -specifically on an individual posting, you can still clear the complete -transaction by typing ‘C-c C-e’. - - -File: ledger-mode.info, Node: Formatting Transactions, Next: Deleting Transactions, Prev: Marking Transactions, Up: The Ledger Buffer - -2.6 Formatting Transactions -=========================== - -When editing a transaction, liberal use of the ‘TAB’ key can keep the -transaction well formatted. If you want to have Ledger-mode cleanup the -formatting of a transaction you can use ‘Align Transaction’ or ‘Align -Region’ from the menu bar. - - The menu item ‘Clean-up Buffer’ sorts all transactions in the buffer -by date, removes extraneous empty lines and aligns every transaction. - - -File: ledger-mode.info, Node: Deleting Transactions, Next: Sorting Transactions, Prev: Formatting Transactions, Up: The Ledger Buffer - -2.7 Deleting Transactions -========================= - -Along with normal buffer editing methods to delete text, Ledger-mode -provides an easy way to delete the transaction under point: ‘C-c C-d’. -The advantage to using this method is that the complete transaction -operation is in the undo buffer. - - -File: ledger-mode.info, Node: Sorting Transactions, Next: Narrowing Transactions, Prev: Deleting Transactions, Up: The Ledger Buffer - -2.8 Sorting Transactions -======================== - -As you operating on the Ledger files, they may become disorganized. For -the most part, Ledger doesn’t care, but our human brains prefer a bit of -order. Sorting the transactions in a buffer into chronological order -can help bring order to chaos. Either using ‘Sort Region’ menu entry or -typing ‘C-c C-s’ will sort all of the transactions in a region by date. -Ledger-mode isn’t particularly smart about handling dates and it simply -sorts the transactions using the string at the beginning of the -transaction. So, you should use the preferred ISO 8601 standard date -format ‘YYYY/MM/DD’ which easily sorts. - - Note, there is a menu entry ‘Sort Buffer’ to sort the entire buffer. -Special transactions like automated transaction, will be moved in the -sorting process and may not function correctly afterwards. For this -reason there is no key sequence. - - You can limit the allowed sort region by using embedded Ledger-mode -markup within your ledger. For example: - - <<< information to not sort >>> - - ; Ledger-mode: Start sort - - <<< transactions to sort >>> - - ; Ledger-mode: End sort - - <<< information to not sort >>> - - You can use menu entries ‘Mark Sort Beginning’ to insert start and -‘Mark Sort End’ to insert end markers. These functions will -automatically delete old markers and put new marker at point. - - -File: ledger-mode.info, Node: Narrowing Transactions, Prev: Sorting Transactions, Up: The Ledger Buffer - -2.9 Narrowing Transactions -========================== - -Often you will want to run Ledger register reports just to look at a -specific set of transactions. If you don’t need the running total -calculation handled by Ledger, Ledger-mode provides a rapid way of -narrowing what is displayed in the buffer in a way that is simpler than -the Ledger register command. - - Based on the Emacs Occur mode by Alexey Veretennikov, Ledger-occur -hides all transactions that do _not_ meet a specific regular expression. -The regular expression can match on any part of the transaction. If you -want to find all transactions whose amount ends in ‘.37’, you can do -that (I don’t know why, but hey, whatever ever floats you aerostat). - - Using ‘C-c C-f’ or the ‘Narrow to Regex’ menu entry, enter a regular -expression in the Minibuffer. Ledger-mode will hide all other -transactions. For details of the regular expression syntax, see your -Emacs documentation. A few examples using the ‘demo.ledger’ are given -here: - -‘Groceries’ - Show only transactions that have a posting to the ‘Groceries’ - account. - -‘^2011/01’ - Show only transactions occurring in January of 2011. - -‘^2011/.*/25’ - Show only transactions occurring on the 25th of the month in 2011. - -‘auto’ - Show only transactions with payees or accounts or comments - containing. ‘auto’ - -‘harley$’ - Show only transactions with any line ending with ‘harley’. - - To show back all transactions simply invoke ‘Narrow to Regex’ or ‘C-c -C-f’ again. - - If you’ve edited some transactions after narrowing such that they -would no longer match the regular expression, you can refresh the -narrowed view using ‘C-c C-g’. - - -File: ledger-mode.info, Node: The Reconcile Buffer, Next: The Report Buffer, Prev: The Ledger Buffer, Up: Top - -3 The Reconcile Buffer -********************** - -* Menu: - -* Basics of Reconciliation:: -* Starting a Reconciliation:: -* Mark Transactions Pending:: -* Edit Transactions During Reconciliation:: -* Finalize Reconciliation:: -* Adding and Deleting Transactions during Reconciliation:: -* Changing Reconciliation Account:: -* Changing Reconciliation Target:: - - -File: ledger-mode.info, Node: Basics of Reconciliation, Next: Starting a Reconciliation, Prev: The Reconcile Buffer, Up: The Reconcile Buffer - -3.1 Basics of Reconciliation -============================ - -Even in this relatively modern era, financial transactions do not happen -instantaneously, unless you are paying cash. When you swipe your debit -card the money may take several days to actually come out of your -account, or a check may take several days to _clear_. That is the root -of the difference between _obligating_ funds and _expending_ funds. -Obligation says you have agreed to pay it, the expenditure doesn’t -happen until the money actually leaves your account. Or in the case of -receiving payment, you have an account receivable until the money has -actually made it to you. - - After an account has been reconciled you have verified that all the -transactions in that account have been correctly recorded and all -parties agree. - - -File: ledger-mode.info, Node: Starting a Reconciliation, Next: Mark Transactions Pending, Prev: Basics of Reconciliation, Up: The Reconcile Buffer - -3.2 Starting a Reconciliation -============================= - -To start reconciling an account you must have a target, both the -transactions that you know about and the transactions the bank knows -about. You can get this from a monthly statement, or from checking your -on-line transaction history. It also helps immensely to know the final -cleared balance you are aiming for. - - Use menu ‘Reconcile Account’ or keyboard shortcut ‘C-c C-r’ to start -reconciliation. - - If cursor is on an account, Ledger-mode will propose this account, or -in the Minibuffer, will prompt for an account to reconcile. Hit ‘RET’ -if you are happy with proposed account, or enter ‘Checking’ as example. -Ledger-mode is not particular about what you enter for the account. You -can leave it blank and ‘*Reconcile*’ buffer will show you _all_ -uncleared transactions. - - After you enter the account enter the target amount. It is helpful -to enter an amount with a commodity. You can also leave it blank, you -will be able to clear transactions but not benefit from balance -calculations. It assumes initially that you are using ‘$’ (USD) as your -default commodity. If you are working in a different currency you can -change the default in variable ‘ledger-reconcile-default-commodity’ to -whatever you need. If you work in multiple commodities simply enter the -commoditized amount (for example ‘340 VSDX’, for 340 shares of VSDX). - - Ledger-mode reconcile cannot currently reconcile accounts that have -multiple commodities, such as brokerage accounts. You may use -reconciliation mode to clear transactions, but balance calculations will -not display the complete list of commodities. - - -File: ledger-mode.info, Node: Mark Transactions Pending, Next: Edit Transactions During Reconciliation, Prev: Starting a Reconciliation, Up: The Reconcile Buffer - -3.3 Mark Transactions Pending -============================= - -The ‘*Reconcile*’ buffer will show all the uncleared transactions that -meet the criteria set in the regex. By default uncleared transactions -are shown in red. When you have verified that a transaction has been -correctly and completely recorded by the opposing party, mark the -transaction as pending using the ‘SPC’ bar. Continue this process until -you agree with the opposing party and the difference from your target is -zero. - - -File: ledger-mode.info, Node: Edit Transactions During Reconciliation, Next: Finalize Reconciliation, Prev: Mark Transactions Pending, Up: The Reconcile Buffer - -3.4 Edit Transactions during Reconciliation -=========================================== - -If you find errors during reconciliation. You can visit the transaction -under point in the ‘*Reconcile*’ buffer by hitting the ‘RET’ key. This -will take you to the transaction in the Ledger buffer. When you have -finished editing the transaction, saving the buffer will automatically -return you to the ‘*Reconcile*’ buffer and you can mark the transaction -if appropriate. - - -File: ledger-mode.info, Node: Finalize Reconciliation, Next: Adding and Deleting Transactions during Reconciliation, Prev: Edit Transactions During Reconciliation, Up: The Reconcile Buffer - -3.5 Finalize Reconciliation -=========================== - -Once you have marked all transactions as pending and the cleared balance -is correct. Finish the reconciliation by typing ‘C-c C-c’. This marks -all pending transactions as cleared and saves the ledger buffer. - - Type ‘q’ to close out the reconciliation buffer. If variable -LEDGER-RECONCILE-FINISH-FORCE-QUIT is set, the reconciliation buffer -will be killed automatically after ‘C-c C-c’. - - -File: ledger-mode.info, Node: Adding and Deleting Transactions during Reconciliation, Next: Changing Reconciliation Account, Prev: Finalize Reconciliation, Up: The Reconcile Buffer - -3.6 Adding and Deleting Transactions during Reconciliation -========================================================== - -While reconciling, you may find new transactions that need to be entered -into your ledger. Simply type ‘a’ to bring up the quick add for the -ledger buffer. - - Typing ‘d’ will delete the transaction under point in the -‘*Reconcile*’ buffer from the ledger buffer. - - -File: ledger-mode.info, Node: Changing Reconciliation Account, Next: Changing Reconciliation Target, Prev: Adding and Deleting Transactions during Reconciliation, Up: The Reconcile Buffer - -3.7 Changing Reconciliation Account -=================================== - -You can conveniently switch the account being reconciled by typing ‘g’, -and entering a new account to reconcile. This simply restarts the -reconcile process. Any transactions that were marked _pending_ in the -ledger buffer are left in that state when the account is switched. - - -File: ledger-mode.info, Node: Changing Reconciliation Target, Prev: Changing Reconciliation Account, Up: The Reconcile Buffer - -3.8 Changing Reconciliation Target -================================== - -If for some reason during reconciliation your target amount changes, -type ‘t’ and enter the new target value. - - -File: ledger-mode.info, Node: The Report Buffer, Next: Scheduling Transactions, Prev: The Reconcile Buffer, Up: Top - -4 The Report Buffer -******************* - -* Menu: - -* Running Basic Reports:: -* Adding and Editing Reports:: -* Reversing Report Order:: - - -File: ledger-mode.info, Node: Running Basic Reports, Next: Adding and Editing Reports, Prev: The Report Buffer, Up: The Report Buffer - -4.1 Running Reports -=================== - -The real power behind Ledger is in its amazing reporting capability. -Ledger-mode provides easy facility to run reports directly from Emacs. -It has four reports built-in and facilities for adding custom reports. - - Typing ‘C-c C-o C-r’ or using menu ‘Run Report’ prompts for the name -of a saved report. The built-in reports are: - -BAL - Produce a balance reports of all accounts. - -REG - Produce a register report of all transactions. - -PAYEE - Prompt for a payee, then produce a register report of all - transactions involving that payee. - -ACCOUNT - Prompt for an account, then produce a register report of all - transactions involving that account. - - While viewing reports you can easily switch back and forth between -the ledger buffer and the ‘*Ledger Report*’ buffer. In ‘*Ledger -Report*’ buffer, typing ‘RET’ will take you to that transaction in the -ledger buffer. While in the ledger buffer ‘C-c C-o C-g’ returns you to -the ‘*Ledger Report*’ buffer. - - By default Ledger-mode will refresh the report buffer when the ledger -buffer is saved. If you want to rerun the report at another time ‘C-c -C-o C-a’. This is useful if you have other programs altering your -ledger file outside of Emacs. - - -File: ledger-mode.info, Node: Adding and Editing Reports, Next: Reversing Report Order, Prev: Running Basic Reports, Up: The Report Buffer - -4.2 Adding and Editing Reports -============================== - -* Menu: - -* Expansion Formats:: -* Make Report Transactions Active:: - -If you type a report name that Ledger-mode doesn’t recognize it will -prompt you for a ledger command line to run. That command is -automatically saved with the name given and you can re-run it at any -time. - - There are two ways to edit the command line for a report. The first -is to provide a prefix argument to the run-report command. For example, -type ‘M-1 C-c C-o C-r’. This will prompt you for the report name, then -present the report command line to be edited. When you hit ‘RET’, the -report will be run, but it will not be permanently saved. If you want -to save it, type ‘S’ in the ‘*Ledger Report*’ buffer you will have the -option to give it a new name, or overwrite the old report. - - Deleting reports is accomplished by typing ‘C-c C-o C-e’ or using -‘Edit Report’ menu in the ledger buffer, or typing ‘e’ in the ‘*Ledger -Report*’ buffer. This takes you to the Emacs customization window for -the Ledger Reports variables. Use the widgets to delete the report you -want removed. - - Typing ‘C-c C-o C-s’ will prompt for a name and save the current -report. - - -File: ledger-mode.info, Node: Expansion Formats, Next: Make Report Transactions Active, Prev: Adding and Editing Reports, Up: Adding and Editing Reports - -4.2.1 Expansion Formats ------------------------ - -It is sometimes convenient to leave room to customize a report without -saving the command line every time. For example running a register -report for a specific account entered at runtime by the user. The -built-in report ACCOUNT does exactly that, using a variable expansion to -prompt the user for the account to use. There are four variables that -can be expanded to run a report: - -LEDGER-FILE - Returns the file to be operated on. - -PAYEE - Prompts for a payee. - -ACCOUNT - Prompt for an account. - -TAGNAME - Prompt for a meta-data tag name. - -TAGVALUE - Prompt for a meta-data tag value. - -MONTH - Return the current month. - - You can use these expansion values in your ledger report commands. -For example, if you wanted to specify a register report the displayed -transactions from a user-determined account with a particular meta-data -tag value, you specify the following command line: - - ledger -f %(ledger-file) reg %(account) \ - --limit \"tag('my-tag') =~/%(value)/\" - - Note how the double-quotes are escaped with back-slashes. - - Additionally, if you want a report showing a particular month and be -able to easily change that month, you can specify a period using the -‘%(month)’ specifier like this: - - ledger -f %(ledger-file) balance --period %(month) ^Income ^Expenses - - When you do this, you can use ‘M-p’ or ‘M-n’ to re-open the same -report with the previous or next month. - - -File: ledger-mode.info, Node: Make Report Transactions Active, Prev: Expansion Formats, Up: Adding and Editing Reports - -4.2.2 Make Report Transactions Active -------------------------------------- - -In a large register report it is convenient to be able to jump to the -source transaction. Ledger-mode will automatically include source -information in every register file that doesn’t contain a ‘--subtotal’ -option. It does this by adding -‘--prepend-format='%(filename):%(beg_line):'’ to the register report -command-line you specify. You should never have to see this, but if -there is an error in your ledger output this additional information may -not get stripped out of the visible report. - - -File: ledger-mode.info, Node: Reversing Report Order, Prev: Adding and Editing Reports, Up: The Report Buffer - -4.3 Reversing Report Order -========================== - -Often, banks show their on-line transaction histories with the most -recent transaction at the top. Ledger itself cannot do a sensible -ledger report in reverse chronological order, if you sort on reverse -date the calculation will also run in the opposite direction. If you -want to compare a ledger register report to a bank report with the most -recent transactions at the top, type ‘R’ in the ‘*Ledger Report*’ buffer -and it will reverse the order of the transactions and maintain the -proper mathematical sense. - - -File: ledger-mode.info, Node: Scheduling Transactions, Next: Customizing Ledger-mode, Prev: The Report Buffer, Up: Top - -5 Scheduling Transactions -************************* - -The Ledger program provides for automating transactions but these -transaction aren’t _real_, they only exist inside a ledger session and -are not reflected in the actual data file. Many transactions are very -repetitive, but may vary slightly in the date they occur on, or the -amount. Some transactions are weekly, monthly, quarterly or annually. -Ledger mode provides a way to schedule upcoming transaction with a -flexible scheduler that allows you to specify the transactions in a -separate ledger file and calculate the upcoming occurrences of those -transactions. You can then copy the transactions into your live data -file. - -* Menu: - -* Specifying Upcoming Transactions:: - - -File: ledger-mode.info, Node: Specifying Upcoming Transactions, Prev: Scheduling Transactions, Up: Scheduling Transactions - -5.1 Specifying Upcoming Transactions -==================================== - -The format for specifying transactions is identical to Ledger’s file -format with the exception of the date field. The data field is modified -by surrounding it with brackets and using wild cards and special -characters to specify when the transactions should appear. - -* Menu: - -* Transactions that occur on specific dates:: -* Transactions that occur on specific days:: - - -File: ledger-mode.info, Node: Transactions that occur on specific dates, Next: Transactions that occur on specific days, Prev: Specifying Upcoming Transactions, Up: Specifying Upcoming Transactions - -5.1.1 Transactions that occur on specific dates ------------------------------------------------ - -Many times you will enter repetitive transactions that occur on the same -day of the month each month. These can be specified using a wild card -in the year and month with a fixed date in the day. The following entry -specifies a transaction that occurs on the first and fifteenth of every -month in every year. - [*/*/1,15] Paycheck - Income:Job $1000.00 - Assets:Checking - - Some transactions do not occur every month. Comma separated lists of -the months, or ‘E’ for even, or ‘O’ for odd number months can also be -specified. The following entry specifies a bi-monthly exterminator bill -that occurs in the even months: - [*/E/01] Exterminator - Expenses:Home $100.00 - Assets:Checking - - -File: ledger-mode.info, Node: Transactions that occur on specific days, Prev: Transactions that occur on specific dates, Up: Specifying Upcoming Transactions - -5.1.2 Transactions that occur on specific days ----------------------------------------------- - -Some transactions occur every relative to the day of the week rather -than the date of the month. For example, many people are paid every two -weeks without regard to the day of the month. Other events may occur on -specific days regardless of the date. For example the following -transactions creates a transaction every other Thursday: - - [2014/11/27+2Th] Paycheck - Income:Job $1000.00 - Assets:Checking - - It is necessary to specify a starting date in order for this type of -recurrence relation to be specified. The day names are two character -codes that default to Mo, Tu, We, Th, Fr, Sa, Su, for Monday, Tuesday, -Wednesday, Thursday, Friday, Saturday, Sunday respectively. You can -change the codes to something more convenient for your locale by -customizing the ledger ‘ledger-schedule-week-days’. They must be two -characters long. - - -File: ledger-mode.info, Node: Customizing Ledger-mode, Next: Generating Ledger Regression Tests, Prev: Scheduling Transactions, Up: Top - -6 Customizing Ledger-mode -************************* - -* Menu: - -* Ledger-mode Customization:: -* Customization Variables:: - - -File: ledger-mode.info, Node: Ledger-mode Customization, Next: Customization Variables, Prev: Customizing Ledger-mode, Up: Customizing Ledger-mode - -6.1 Ledger-mode Customization -============================= - -Ledger-mode has several options available for configuration. All -options can be configured through the Emacs customization menus, or -specified in your Emacs initialization file. The complete list of -options is shown below. To change the option using the Emacs -customization menu, simply choose customize in the Options menu and look -for Ledger under the data options. Alternatively you can choose -‘Customize Specific Group’ and enter ‘Ledger’ as the group. - - -File: ledger-mode.info, Node: Customization Variables, Prev: Ledger-mode Customization, Up: Customizing Ledger-mode - -6.2 Customization Variables -=========================== - -* Menu: - -* Ledger Customization Group:: -* Ledger Reconcile Customization Group:: -* Ledger Report Customization Group:: -* Ledger Faces Customization Group:: -* Ledger Post Customization Group:: -* Ledger Exec Customization Group:: -* Ledger Test Customization Group:: -* Ledger Texi Customization Group:: - - -File: ledger-mode.info, Node: Ledger Customization Group, Next: Ledger Reconcile Customization Group, Prev: Customization Variables, Up: Customization Variables - -6.2.1 Ledger Customization Group --------------------------------- - -‘ledger-occur-use-face-shown’ - If non-nil, use a custom face for transactions shown in - ‘ledger-occur’ mode using ‘ledger-occur-xact-face’. - -‘ledger-clear-whole-transactions’ - If non-nil, clear whole transactions, not individual postings. - -‘ledger-highlight-xact-under-point’ - If non-nil, highlight transaction under point using - ‘ledger-font-highlight-face’. - - -File: ledger-mode.info, Node: Ledger Reconcile Customization Group, Next: Ledger Report Customization Group, Prev: Ledger Customization Group, Up: Customization Variables - -6.2.2 Ledger Reconcile Customization Group ------------------------------------------- - -‘ledger-reconcile-buffer-name’ - Name to use for reconciliation buffer. Defaults to ‘*Reconcile*’. - -‘ledger-narrow-on-reconcile’ - If t, limit transactions shown in main buffer to those matching the - reconcile regex. - -‘ledger-buffer-tracks-reconcile-buffer’ - If t, then when the cursor is moved to a new transaction in the - ‘*Reconcile*’ buffer. Then that transaction will be shown in its - source buffer. - -‘ledger-reconcile-force-window-bottom’ - If t, make the ‘*Reconcile*’ window appear along the bottom of the - register window and resize. - -‘ledger-reconcile-toggle-to-pending’ - If t, then toggle between uncleared and pending ‘!’. If false - toggle between uncleared and cleared ‘*’. - -‘ledger-reconcile-default-date-format’ - Date format for the reconcile buffer. Defaults to - ‘ledger-default-date-format’. - -‘ledger-reconcile-target-prompt-string’ - Prompt for reconcile target. Defaults to "Target amount for - reconciliation ". - -‘ledger-reconcile-buffer-header’ - Header string for the reconcile buffer. If non-nil, the name of - the account being reconciled will be substituted into the ’%s’. If - nil, no header will be displayed. Defaults to "Reconciling account - %s\n\n". - -‘ledger-reconcile-buffer-line-format’ - Format string for the ledger reconcile posting format. Available - fields are date, status, code, payee, account, amount. The format - for each field is %WIDTH(FIELD), WIDTH can be preceded by a minus - sign which mean to left justify and pad the field. WIDTH is the - minimum number of characters to display; if string is longer, it is - not truncated unless ‘ledger-reconcile-buffer-payee-max-chars’ or - ‘ledger-reconcile-buffer-account-max-chars’ is defined. Defaults - to "%(date)s %-4(code)s %-50(payee)s %-30(account)s %15(amount)s\n" - -‘ledger-reconcile-buffer-payee-max-chars’ - If positive, truncate payee name right side to max number of - characters. - -‘ledger-reconcile-buffer-account-max-chars’ - If positive, truncate account name left side to max number of - characters. - -‘ledger-reconcile-sort-key’ - Key for sorting reconcile buffer. Possible values are ’(date)’, - ’(amount)’, ’(payee)’ or ’(0)’ for no sorting, i.e. using ledger - file order. Defaults to ’(0)’. - -‘ledger-reconcile-insert-effective-date nil’ - If t, prompt for effective date when clearing transactions during - reconciliation. - -‘ledger-reconcile-finish-force-quit nil’ - If t, will force closing reconcile window after ‘C-c C-c’. - - -File: ledger-mode.info, Node: Ledger Report Customization Group, Next: Ledger Faces Customization Group, Prev: Ledger Reconcile Customization Group, Up: Customization Variables - -6.2.3 Ledger Report Customization Group ---------------------------------------- - -‘ledger-reports’ - Definition of reports to run. - -‘ledger-report-format-specifiers’ - An alist mapping ledger report format specifiers to implementing - functions. - -‘ledger-report-use-native-highlighting’ - Whether reports should be displayed using the same colors as when - calling ledger on the command line. - -‘ledger-report-auto-width’ - Whether reports should fill the whole width of the ‘*Report*’ - window - - -File: ledger-mode.info, Node: Ledger Faces Customization Group, Next: Ledger Post Customization Group, Prev: Ledger Report Customization Group, Up: Customization Variables - -6.2.4 Ledger Faces Customization Group --------------------------------------- - -Ledger Faces: Ledger-mode highlighting - -‘ledger-font-uncleared-face’ - Default face for Ledger. - -‘ledger-font-cleared-face’ - Default face for cleared ‘*’ transactions. - -‘ledger-font-highlight-face’ - Default face for transaction under point. - -‘ledger-font-pending-face’ - Default face for pending ‘!’ transactions. - -‘ledger-font-other-face’ - Default face for other transactions. - -‘ledger-font-posting-account-face’ - Face for Ledger accounts. - -‘ledger-font-posting-account-cleared-face’ - Face for cleared Ledger accounts. - -‘ledger-font-posting-account-pending-face’ - Face for Ledger pending accounts. - -‘ledger-font-posting-amount-face’ - Face for Ledger amounts. - -‘ledger-occur-narrowed-face’ - Default face for Ledger occur mode hidden transactions. - -‘ledger-occur-xact-face’ - Default face for Ledger occur mode shown transactions. - -‘ledger-font-comment-face’ - Face for Ledger comments. - -‘ledger-font-reconciler-uncleared-face’ - Default face for uncleared transactions in the ‘*Reconcile*’ - buffer. - -‘ledger-font-reconciler-cleared-face’ - Default face for cleared ‘*’ transactions in the ‘*Reconcile*’ - buffer. - -‘ledger-font-reconciler-pending-face’ - Default face for pending ‘!’ transactions in the ‘*Reconcile*’ - buffer. - -‘ledger-font-report-clickable-face’ - Face applied to clickable entries in the ‘*Report*’ buffer. - - -File: ledger-mode.info, Node: Ledger Post Customization Group, Next: Ledger Exec Customization Group, Prev: Ledger Faces Customization Group, Up: Customization Variables - -6.2.5 Ledger Post Customization Group -------------------------------------- - -Ledger Post: - -‘ledger-post-account-alignment-column’ - The column Ledger-mode attempts to align accounts to. - -‘ledger-post-amount-alignment-at’ - Position at which the amount is aligned. - - Can be ‘:end’ to align on the last number of the amount (can be - followed by unaligned commodity) or ‘:decimal’ to align at the - decimal separator. - -‘ledger-post-amount-alignment-column’ - The column Ledger-mode attempts to align amounts to. - -‘ledger-post-auto-align’ - When non-nil, realign post amounts when indenting or completing. - - -File: ledger-mode.info, Node: Ledger Exec Customization Group, Next: Ledger Test Customization Group, Prev: Ledger Post Customization Group, Up: Customization Variables - -6.2.6 Ledger Exec Customization Group -------------------------------------- - -Ledger Exec: Interface to the Ledger command-line accounting program. - -‘ledger-binary-path’ - Path to the ledger executable. - -‘ledger-init-file-name’ - Location of the ledger initialization file. nil if you don’t have - one. - - -File: ledger-mode.info, Node: Ledger Test Customization Group, Next: Ledger Texi Customization Group, Prev: Ledger Exec Customization Group, Up: Customization Variables - -6.2.7 Ledger Test Customization Group -------------------------------------- - -‘ledger-source-directory’ - Directory where the Ledger sources are located. - -‘ledger-test-binary’ - Directory where the debug binary. - - -File: ledger-mode.info, Node: Ledger Texi Customization Group, Prev: Ledger Test Customization Group, Up: Customization Variables - -6.2.8 Ledger Texi Customization Group -------------------------------------- - -‘ledger-texi-sample-doc-path’ - Location for sample data to be used in texi tests, defaults to - ‘~/ledger/doc/sample.dat’. - -‘ledger-texi-normalization-args’ - texi normalization for producing ledger output, defaults to - ‘--args-only --columns 80’. - - -File: ledger-mode.info, Node: Generating Ledger Regression Tests, Next: Embedding Example results in Ledger Documentation, Prev: Customizing Ledger-mode, Up: Top - -7 Generating Ledger Regression Tests -************************************ - -Work in Progress. - - -File: ledger-mode.info, Node: Embedding Example results in Ledger Documentation, Next: Hacking Ledger-mode, Prev: Generating Ledger Regression Tests, Up: Top - -8 Embedding Example results in Ledger Documentation -*************************************************** - -Work in Progress. - - -File: ledger-mode.info, Node: Hacking Ledger-mode, Next: Concept Index, Prev: Embedding Example results in Ledger Documentation, Up: Top - -9 Hacking Ledger-mode -********************* - -* Menu: - -* Use org-like outlines:: - - -File: ledger-mode.info, Node: Use org-like outlines, Prev: Hacking Ledger-mode, Up: Hacking Ledger-mode - -9.1 Use org-like outlines -========================= - -Some users like to have org-like outlines for their ledger files. A -suggested customization is to include something like the following in -your Emacs configuration: - - (eval-after-load 'ledger-mode - (progn - ;; org-cycle allows completion to work whereas outline-toggle-children does not - (define-key ledger-mode-map (kbd "TAB") #'org-cycle) - (add-hook 'ledger-mode-hook #'outline-minor-mode) - (font-lock-add-keywords 'ledger-mode outline-font-lock-keywords))) - - -File: ledger-mode.info, Node: Concept Index, Next: Command & Variable Index, Prev: Hacking Ledger-mode, Up: Top - -Concept Index -************* - - -* Menu: - -* balance: Quick Balance Display. - (line 6) -* Calc: Editing Amounts. (line 6) -* cleared: Marking Transactions. (line 6) -* customization, executable: Ledger Exec Customization Group. - (line 6) -* customization, faces: Ledger Faces Customization Group. - (line 6) -* customization, ledger-mode: Ledger Customization Group. - (line 6) -* customization, post: Ledger Post Customization Group. - (line 6) -* customization, reconcile: Ledger Reconcile Customization Group. - (line 6) -* customization, report: Ledger Report Customization Group. - (line 6) -* customization, test: Ledger Test Customization Group. - (line 6) -* customization, texi: Ledger Texi Customization Group. - (line 6) -* demo: Quick Demo. (line 6) -* effective date: Setting a Transactions Effective Date. - (line 6) -* folding: Use org-like outlines. - (line 10) -* GNU Emacs Calculator: Editing Amounts. (line 6) -* installation: Quick Installation. (line 6) -* menu: Menus. (line 6) -* org: Use org-like outlines. - (line 10) -* outline: Use org-like outlines. - (line 10) -* pending: Marking Transactions. (line 6) -* reconciliation, account changing: Changing Reconciliation Account. - (line 6) -* reconciliation, basics: Basics of Reconciliation. - (line 6) -* reconciliation, finalizing: Finalize Reconciliation. - (line 6) -* reconciliation, starting: Starting a Reconciliation. - (line 6) -* reconciliation, target changing: Changing Reconciliation Target. - (line 6) -* reconciliation, transaction adding and deleting: Adding and Deleting Transactions during Reconciliation. - (line 6) -* reconciliation, transaction editing: Edit Transactions During Reconciliation. - (line 6) -* reconciliation, transaction marking: Mark Transactions Pending. - (line 6) -* report, adding and editing: Adding and Editing Reports. - (line 6) -* report, custom command: Make Report Transactions Active. - (line 6) -* report, custom variable: Expansion Formats. (line 6) -* report, order reversing: Reversing Report Order. - (line 6) -* report, running: Running Basic Reports. - (line 6) -* transaction, adding: Adding Transactions. (line 6) -* transaction, copying: Copying Transactions. (line 6) -* transaction, deleting: Deleting Transactions. - (line 6) -* transaction, display filtering: Narrowing Transactions. - (line 6) -* transaction, editing amounts: Editing Amounts. (line 6) -* transaction, formatting: Formatting Transactions. - (line 6) -* transaction, marking: Marking Transactions. (line 6) -* transaction, narrowing: Narrowing Transactions. - (line 6) -* transaction, navigation: Navigating Transactions. - (line 6) -* transaction, sorting: Sorting Transactions. (line 6) -* uncleared: Marking Transactions. (line 6) - - -File: ledger-mode.info, Node: Command & Variable Index, Next: Keystroke Index, Prev: Concept Index, Up: Top - -Command & Variable Index -************************ - - -* Menu: - -* ledger-binary-path: Ledger Exec Customization Group. - (line 8) -* ledger-buffer-tracks-reconcile-buffer: Ledger Reconcile Customization Group. - (line 13) -* ledger-clear-whole-transactions: Ledger Customization Group. - (line 10) -* ledger-font-cleared-face: Ledger Faces Customization Group. - (line 11) -* ledger-font-comment-face: Ledger Faces Customization Group. - (line 41) -* ledger-font-highlight-face: Ledger Faces Customization Group. - (line 14) -* ledger-font-other-face: Ledger Faces Customization Group. - (line 20) -* ledger-font-pending-face: Ledger Faces Customization Group. - (line 17) -* ledger-font-posting-account-cleared-face: Ledger Faces Customization Group. - (line 26) -* ledger-font-posting-account-face: Ledger Faces Customization Group. - (line 23) -* ledger-font-posting-account-pending-face: Ledger Faces Customization Group. - (line 29) -* ledger-font-posting-amount-face: Ledger Faces Customization Group. - (line 32) -* ledger-font-reconciler-cleared-face: Ledger Faces Customization Group. - (line 48) -* ledger-font-reconciler-pending-face: Ledger Faces Customization Group. - (line 52) -* ledger-font-reconciler-uncleared-face: Ledger Faces Customization Group. - (line 44) -* ledger-font-report-clickable-face: Ledger Faces Customization Group. - (line 56) -* ledger-font-uncleared-face: Ledger Faces Customization Group. - (line 8) -* ledger-highlight-xact-under-point: Ledger Customization Group. - (line 13) -* ledger-init-file-name: Ledger Exec Customization Group. - (line 11) -* ledger-narrow-on-reconcile: Ledger Reconcile Customization Group. - (line 9) -* ledger-navigate-next-uncleared: Navigating Transactions. - (line 10) -* ledger-navigate-next-xact-or-directive: Navigating Transactions. - (line 6) -* ledger-navigate-prev-xact-or-directive: Navigating Transactions. - (line 6) -* ledger-navigate-previous-uncleared: Navigating Transactions. - (line 10) -* ledger-occur-narrowed-face: Ledger Faces Customization Group. - (line 35) -* ledger-occur-use-face-shown: Ledger Customization Group. - (line 6) -* ledger-occur-xact-face: Ledger Faces Customization Group. - (line 38) -* ledger-post-account-alignment-column: Ledger Post Customization Group. - (line 8) -* ledger-post-amount-alignment-at: Ledger Post Customization Group. - (line 11) -* ledger-post-amount-alignment-column: Adding Transactions. (line 6) -* ledger-post-amount-alignment-column <1>: Ledger Post Customization Group. - (line 18) -* ledger-post-auto-align: Ledger Post Customization Group. - (line 21) -* ledger-reconcile-buffer-account-max-chars: Ledger Reconcile Customization Group. - (line 54) -* ledger-reconcile-buffer-header: Ledger Reconcile Customization Group. - (line 34) -* ledger-reconcile-buffer-line-format: Ledger Reconcile Customization Group. - (line 40) -* ledger-reconcile-buffer-name: Ledger Reconcile Customization Group. - (line 6) -* ledger-reconcile-buffer-payee-max-chars: Ledger Reconcile Customization Group. - (line 50) -* ledger-reconcile-default-commodity: Starting a Reconciliation. - (line 6) -* ledger-reconcile-default-date-format: Ledger Reconcile Customization Group. - (line 26) -* ledger-reconcile-finish-force-quit nil: Ledger Reconcile Customization Group. - (line 67) -* ledger-reconcile-force-window-bottom: Ledger Reconcile Customization Group. - (line 18) -* ledger-reconcile-insert-effective-date nil: Ledger Reconcile Customization Group. - (line 63) -* ledger-reconcile-sort-key: Ledger Reconcile Customization Group. - (line 58) -* ledger-reconcile-target-prompt-string: Ledger Reconcile Customization Group. - (line 30) -* ledger-reconcile-toggle-to-pending: Ledger Reconcile Customization Group. - (line 22) -* ledger-report-auto-width: Ledger Report Customization Group. - (line 17) -* ledger-report-format-specifiers: Ledger Report Customization Group. - (line 9) -* ledger-report-use-native-highlighting: Ledger Report Customization Group. - (line 13) -* ledger-reports: Adding and Editing Reports. - (line 6) -* ledger-reports <1>: Ledger Report Customization Group. - (line 6) -* ledger-source-directory: Ledger Test Customization Group. - (line 6) -* ledger-test-binary: Ledger Test Customization Group. - (line 9) -* ledger-texi-normalization-args: Ledger Texi Customization Group. - (line 10) -* ledger-texi-sample-doc-path: Ledger Texi Customization Group. - (line 6) - - -File: ledger-mode.info, Node: Keystroke Index, Prev: Command & Variable Index, Up: Top - -Keystroke Index -*************** - - -* Menu: - -* a: Adding and Deleting Transactions during Reconciliation. - (line 6) -* C-c C-a: Quick Add. (line 6) -* C-c C-b: Editing Amounts. (line 6) -* C-c C-c: Reconciliation. (line 6) -* C-c C-c <1>: Reports. (line 6) -* C-c C-c <2>: Marking Transactions. (line 28) -* C-c C-c <3>: Edit Transactions During Reconciliation. - (line 6) -* C-c C-c <4>: Finalize Reconciliation. - (line 6) -* C-c C-d: Deleting Transactions. - (line 6) -* C-c C-e: Marking Transactions. (line 28) -* C-c C-f: Narrowing. (line 6) -* C-c C-f <1>: Narrowing Transactions. - (line 6) -* C-c C-g: Narrowing. (line 6) -* C-c C-g <1>: Narrowing Transactions. - (line 6) -* C-c C-k: Copying Transactions. (line 6) -* C-c C-o C-a: Running Basic Reports. - (line 6) -* C-c C-o C-e: Adding and Editing Reports. - (line 6) -* C-c C-o C-g: Running Basic Reports. - (line 6) -* C-c C-o C-r: Reports. (line 6) -* C-c C-o C-r <1>: Running Basic Reports. - (line 6) -* C-c C-p: Quick Balance Display. - (line 6) -* C-c C-r: Reconciliation. (line 6) -* C-c C-r <1>: Starting a Reconciliation. - (line 6) -* C-c C-s: Sorting Transactions. (line 6) -* C-c C-t: Setting a Transactions Effective Date. - (line 6) -* C-c TAB: Quick Add. (line 6) -* d: Adding and Deleting Transactions during Reconciliation. - (line 6) -* e: Adding and Editing Reports. - (line 6) -* g: Changing Reconciliation Account. - (line 6) -* M-1 C-c C-o C-r: Adding and Editing Reports. - (line 6) -* M-n: Navigating Transactions. - (line 6) -* M-p: Navigating Transactions. - (line 6) -* q: Reconciliation. (line 6) -* q <1>: Finalize Reconciliation. - (line 6) -* R: Reversing Report Order. - (line 6) -* RET: Edit Transactions During Reconciliation. - (line 6) -* S: Adding and Editing Reports. - (line 6) -* SPC: Reconciliation. (line 6) -* SPC <1>: Mark Transactions Pending. - (line 6) -* t: Changing Reconciliation Target. - (line 6) -* TAB: Adding Transactions. (line 6) -* y: Editing Amounts. (line 6) - - - -Tag Table: -Node: Top1742 -Node: Introduction to Ledger-mode2555 -Node: Quick Installation2784 -Node: Menus3716 -Node: Quick Demo4031 -Node: Quick Add4461 -Node: Reconciliation5559 -Node: Reports7243 -Node: Narrowing8273 -Node: The Ledger Buffer8857 -Node: Navigating Transactions9263 -Node: Adding Transactions9823 -Node: Setting a Transactions Effective Date11322 -Node: Quick Balance Display12222 -Node: Copying Transactions12754 -Node: Editing Amounts13356 -Node: Marking Transactions14427 -Node: Formatting Transactions16122 -Node: Deleting Transactions16720 -Node: Sorting Transactions17160 -Node: Narrowing Transactions18712 -Node: The Reconcile Buffer20562 -Node: Basics of Reconciliation21027 -Node: Starting a Reconciliation21976 -Node: Mark Transactions Pending23825 -Node: Edit Transactions During Reconciliation24494 -Node: Finalize Reconciliation25137 -Node: Adding and Deleting Transactions during Reconciliation25794 -Node: Changing Reconciliation Account26378 -Node: Changing Reconciliation Target26928 -Node: The Report Buffer27246 -Node: Running Basic Reports27504 -Node: Adding and Editing Reports28937 -Node: Expansion Formats30324 -Node: Make Report Transactions Active31965 -Node: Reversing Report Order32672 -Node: Scheduling Transactions33365 -Node: Specifying Upcoming Transactions34221 -Node: Transactions that occur on specific dates34795 -Node: Transactions that occur on specific days35836 -Node: Customizing Ledger-mode36965 -Node: Ledger-mode Customization37229 -Node: Customization Variables37914 -Node: Ledger Customization Group38394 -Node: Ledger Reconcile Customization Group39034 -Node: Ledger Report Customization Group41985 -Node: Ledger Faces Customization Group42704 -Node: Ledger Post Customization Group44451 -Node: Ledger Exec Customization Group45278 -Node: Ledger Test Customization Group45777 -Node: Ledger Texi Customization Group46179 -Node: Generating Ledger Regression Tests46671 -Node: Embedding Example results in Ledger Documentation46934 -Node: Hacking Ledger-mode47223 -Node: Use org-like outlines47448 -Node: Concept Index48113 -Node: Command & Variable Index53629 -Node: Keystroke Index61739 - -End Tag Table - - -Local Variables: -coding: utf-8 -End: diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-navigate.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-navigate.el @@ -1,199 +0,0 @@ -;;; ledger-navigate.el --- Provide navigation services through the ledger buffer. -*- lexical-binding: t; -*- - -;; Copyright (C) 2014-2015 Craig Earls (enderw88 AT gmail DOT com) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - - -;;; Commentary: -;; - -;;; Code: - -(require 'ledger-regex) -(require 'ledger-context) - -(defun ledger-navigate-next-xact () - "Move point to beginning of next xact." - ;; make sure we actually move to the next xact, even if we are the - ;; beginning of one now. - (if (looking-at ledger-payee-any-status-regex) - (forward-line)) - (if (re-search-forward ledger-payee-any-status-regex nil t) - (goto-char (match-beginning 0)) - (goto-char (point-max)))) - -(defun ledger-navigate-start-xact-or-directive-p () - "Return t if at the beginning line of an xact or directive. - -Assumes point is at the beginning of a line." - (not (looking-at "[ \t]\\|\\(^$\\)"))) - -(defun ledger-navigate-next-xact-or-directive () - "Move to the beginning of the next xact or directive." - (interactive) - (beginning-of-line) - (if (ledger-navigate-start-xact-or-directive-p) ; if we are the start of an xact, move forward to the next xact - (progn - (forward-line) - (unless (ledger-navigate-start-xact-or-directive-p) ; we have moved forward and are not at another xact, recurse forward - (ledger-navigate-next-xact-or-directive))) - (while (not (or (eobp) ; we didn't start off at the beginning of an xact - (ledger-navigate-start-xact-or-directive-p))) - (forward-line)))) - -(defun ledger-navigate-prev-xact-or-directive () - "Move point to beginning of previous xact." - (interactive) - (let ((context (car (ledger-context-at-point)))) - (when (equal context 'acct-transaction) - (ledger-navigate-beginning-of-xact)) - (beginning-of-line) - (re-search-backward "^[[:graph:]]" nil t))) - -(defun ledger-navigate-beginning-of-xact () - "Move point to the beginning of the current xact." - (interactive) - ;; need to start at the beginning of a line in case we are in the first line of an xact already. - (beginning-of-line) - (let ((sreg (concat "^[=~[:digit:]]"))) - (unless (looking-at sreg) - (re-search-backward sreg nil t) - (beginning-of-line))) - (point)) - -(defun ledger-navigate-end-of-xact () - "Move point to end of xact." - (interactive) - (ledger-navigate-next-xact-or-directive) - (re-search-backward ".$") - (end-of-line) - (point)) - -(defun ledger-navigate-to-line (line-number) - "Rapidly move point to line LINE-NUMBER." - (goto-char (point-min)) - (forward-line (1- line-number))) - -(defun ledger-navigate-find-xact-extents (pos) - "Return list containing point for beginning and end of xact containing POS. -Requires empty line separating xacts." - (interactive "d") - (save-excursion - (goto-char pos) - (list (ledger-navigate-beginning-of-xact) - (ledger-navigate-end-of-xact)))) - -(defun ledger-navigate-skip-lines-backwards (re) - "Move backwards if necessary until the line beginning does not match RE." - (beginning-of-line) - (while (and (looking-at-p re) - (zerop (forward-line -1))))) - -(defun ledger-navigate-skip-lines-forwards (re) - "Move forwards if necessary until the line beginning does not match RE." - (beginning-of-line) - (while (and (looking-at-p re) - (zerop (forward-line 1))))) - -(defun ledger-navigate-find-directive-extents (pos) - "Return the extents of the directive at POS." - (goto-char pos) - (let ((begin (progn (ledger-navigate-skip-lines-backwards "[ \t]\\|end[[:blank:]]+\\(?:comment\\|test\\)") - (point))) - (end (progn (forward-line 1) - (ledger-navigate-skip-lines-forwards "[ \t]") - (1- (point)))) - (comment-re " *;")) - ;; handle block comments here - (goto-char begin) - (cond - ((looking-at comment-re) - (ledger-navigate-skip-lines-backwards comment-re) - ;; We are either at the beginning of the buffer, or we found - ;; a line outside the comment, or both. If we are outside - ;; the comment then we need to move forward a line. - (unless (looking-at comment-re) - (forward-line 1) - (beginning-of-line)) - (setq begin (point)) - (goto-char pos) - (ledger-navigate-skip-lines-forwards comment-re) - (setq end (point))) - ((looking-at "\\(?:comment\\|test\\)\\>") - (setq end (or (save-match-data - (re-search-forward "^end[[:blank:]]+\\(?:comment\\|test\\)\\_>")) - (point-max))))) - (list begin end))) - -(defun ledger-navigate-block-comment (pos) - "Move past the block comment at POS, and return its extents." - (interactive "d") - (goto-char pos) - (let ((begin (progn (beginning-of-line) - (point))) - (end (progn (end-of-line) - (point))) - (comment-re " *;")) - ;; handle block comments here - (beginning-of-line) - (when (looking-at comment-re) - (ledger-navigate-skip-lines-backwards comment-re) - (setq begin (point)) - (goto-char pos) - (beginning-of-line) - (ledger-navigate-skip-lines-forwards comment-re) - (setq end (point))) - (list begin end))) - - -(defun ledger-navigate-find-element-extents (pos) - "Return list containing beginning and end of the entity surrounding POS." - (interactive "d") - (save-excursion - (goto-char pos) - (beginning-of-line) - (ledger-navigate-skip-lines-backwards "[ \t]\\|end[[:blank:]]+\\(?:comment\\|test\\)\\_>") - (if (looking-at "[=~0-9\\[]") - (ledger-navigate-find-xact-extents pos) - (ledger-navigate-find-directive-extents pos)))) - -(defun ledger-navigate-next-uncleared () - "Move point to the next uncleared transaction." - (interactive) - (when (looking-at ledger-payee-uncleared-regex) - (forward-line)) - (if (re-search-forward ledger-payee-uncleared-regex nil t) - (progn (beginning-of-line) - (point)) - (user-error "No next uncleared transactions"))) - -(defun ledger-navigate-previous-uncleared () - "Move point to the previous uncleared transaction." - (interactive) - (when (equal (car (ledger-context-at-point)) 'acct-transaction) - (ledger-navigate-beginning-of-xact)) - (if (re-search-backward ledger-payee-uncleared-regex nil t) - (progn (beginning-of-line) - (point)) - (user-error "No previous uncleared transactions"))) - - -(provide 'ledger-navigate) - -;;; ledger-navigate.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-navigate.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-navigate.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-occur.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-occur.el @@ -1,178 +0,0 @@ -;;; ledger-occur.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - -;;; Commentary: -;; Provide buffer narrowing to ledger mode. Adapted from original loccur -;; mode by Alexey Veretennikov <alexey dot veretennikov at gmail dot -;; com> -;; -;; Adapted to ledger mode by Craig Earls <enderww at gmail dot -;; com> - -;;; Code: - -(require 'cl-lib) -(require 'ledger-navigate) - -(defconst ledger-occur-overlay-property-name 'ledger-occur-custom-buffer-grep) - -(defcustom ledger-occur-use-face-shown t - "If non-nil, use a custom face for xacts shown in `ledger-occur' mode. -This uses `ledger-occur-xact-face'." - :type 'boolean - :group 'ledger) -(make-variable-buffer-local 'ledger-occur-use-face-shown) - - -(defvar ledger-occur-history nil - "History of previously searched expressions for the prompt.") - -(defvar-local ledger-occur-current-regex nil - "Pattern currently applied to narrow the buffer.") - -(defvar ledger-occur-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-g") #'ledger-occur-refresh) - (define-key map (kbd "C-c C-f") #'ledger-occur-mode) - map) - "Keymap used by `ledger-occur-mode'.") - -(define-minor-mode ledger-occur-mode - "A minor mode which display only transactions matching a pattern. -The pattern is given by `ledger-occur-current-regex'." - :init-value nil - :lighter (:eval (format " Ledger-Narrow(%s)" ledger-occur-current-regex)) - :keymap ledger-occur-mode-map - (if (and ledger-occur-current-regex ledger-occur-mode) - (progn (ledger-occur-refresh) - ;; Clear overlays after revert-buffer and similar commands. - (add-hook 'change-major-mode-hook #'ledger-occur-remove-overlays nil t)) - (ledger-occur-remove-overlays) - (message "Showing all transactions"))) - -(defun ledger-occur-refresh () - "Re-apply the current narrowing expression." - (interactive) - (let ((matches (ledger-occur-compress-matches - (ledger-occur-find-matches ledger-occur-current-regex)))) - (if matches - (ledger-occur-create-overlays matches) - (message "No matches found for '%s'" ledger-occur-current-regex) - (ledger-occur-mode -1)))) - -(defun ledger-occur (regex) - "Show only transactions in the current buffer which match REGEX. - -This command hides all xact in the current buffer except those -matching REGEX. If REGEX is nil or empty, turn off any narrowing -currently active." - (interactive - (list (read-regexp "Regexp" (ledger-occur-prompt) 'ledger-occur-history))) - (if (or (null regex) - (zerop (length regex))) ; empty regex, or already have narrowed, clear narrowing - (ledger-occur-mode -1) - (setq ledger-occur-current-regex regex) - (ledger-occur-mode 1))) - -(defun ledger-occur-prompt () - "Return the default value of the prompt. - -Default value for prompt is the active region, if it is one line -long, otherwise it is the word at point." - (if (use-region-p) - (let ((pos1 (region-beginning)) - (pos2 (region-end))) - ;; Check if the start and the of an active region is on - ;; the same line - (if (= (line-number-at-pos pos1) - (line-number-at-pos pos2)) - (buffer-substring-no-properties pos1 pos2))) - (current-word))) - - -(defun ledger-occur-make-visible-overlay (beg end) - "Make an overlay for a visible portion of the buffer, from BEG to END." - (let ((ovl (make-overlay beg end))) - (overlay-put ovl ledger-occur-overlay-property-name t) - (when ledger-occur-use-face-shown - (overlay-put ovl 'font-lock-face 'ledger-occur-xact-face)))) - -(defun ledger-occur-make-invisible-overlay (beg end) - "Make an overlay for an invisible portion of the buffer, from BEG to END." - (let ((ovl (make-overlay beg end))) - (overlay-put ovl ledger-occur-overlay-property-name t) - (overlay-put ovl 'invisible t))) - -(defun ledger-occur-create-overlays (ovl-bounds) - "Create the overlays for the visible transactions. -Argument OVL-BOUNDS contains bounds for the transactions to be left visible." - (ledger-occur-remove-overlays) - (let ((end-of-last-visible (point-min))) - (pcase-dolist (`(,beg ,end) ovl-bounds) - ;; keep newline before xact visible, but do not highlight it with - ;; `ledger-occur-xact-face' - (ledger-occur-make-invisible-overlay end-of-last-visible (1- beg)) - (ledger-occur-make-visible-overlay beg end) - ;; keep newline after xact visible - (setq end-of-last-visible (1+ end))) - (ledger-occur-make-invisible-overlay end-of-last-visible (point-max)))) - -(defun ledger-occur-remove-overlays () - "Remove the transaction hiding overlays." - (interactive) - (remove-overlays (point-min) - (point-max) ledger-occur-overlay-property-name t)) - -(defun ledger-occur-find-matches (regex) - "Return a list of bounds for transactions matching REGEX." - (save-excursion - (goto-char (point-min)) - ;; Set initial values for variables - (let (lines) - ;; Search loop - (while (not (eobp)) - ;; if something found - (when-let ((endpoint (re-search-forward regex nil 'end)) - (bounds (ledger-navigate-find-element-extents endpoint))) - (push bounds lines) - ;; move to the end of the xact, no need to search inside it more - (goto-char (cadr bounds)))) - (nreverse lines)))) - -(defun ledger-occur-compress-matches (buffer-matches) - "Identify sequential xacts to reduce number of overlays required. - -BUFFER-MATCHES should be a list of (BEG END) lists." - (if buffer-matches - (let ((points (list)) - (current-beginning (caar buffer-matches)) - (current-end (cl-cadar buffer-matches))) - (dolist (match (cdr buffer-matches)) - (if (< (- (car match) current-end) 2) - (setq current-end (cadr match)) - (push (list current-beginning current-end) points) - (setq current-beginning (car match)) - (setq current-end (cadr match)))) - (nreverse (push (list current-beginning current-end) points))))) - -(provide 'ledger-occur) - -;;; ledger-occur.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-occur.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-occur.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-post.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-post.el @@ -1,195 +0,0 @@ -;;; ledger-post.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - - -;;; Commentary: -;; Utility functions for dealing with postings. - -(require 'ledger-regex) -(require 'ledger-navigate) - -(declare-function calc-renumber-stack "calc" ()) -(declare-function ledger-string-to-number "ledger-commodities" (str &optional decimal-comma)) - -;;; Code: - -(defgroup ledger-post nil - "Options for controlling how Ledger-mode deals with postings and completion" - :group 'ledger) - -(defcustom ledger-post-account-alignment-column 4 - "The column Ledger-mode attempts to align accounts to." - :type 'integer - :group 'ledger-post - :safe 'integerp) - -(defcustom ledger-post-amount-alignment-column 52 - "The column Ledger-mode attempts to align amounts to." - :type 'integer - :group 'ledger-post - :safe 'integerp) - -(defcustom ledger-post-amount-alignment-at :end - "Position at which the amount is aligned. - -Can be :end to align on the last number of the amount (can be -followed by unaligned commodity) or :decimal to align at the -decimal separator." - :type '(radio (const :tag "align at the end of amount" :end) - (const :tag "align at the decimal separator" :decimal)) - :group 'ledger-post - :safe (lambda (x) (memq x '(:end :decimal)))) - -(defcustom ledger-post-auto-align t - "When non-nil, realign post amounts when indenting or completing." - :type 'boolean - :group 'ledger-post - :package-version '(ledger-mode . "4.0.0") - :safe 'booleanp) - -(defun ledger-next-amount (&optional end) - "Move point to the next amount, as long as it is not past END. -Return the width of the amount field as an integer and leave -point at beginning of the commodity." - ;;(beginning-of-line) - (let ((case-fold-search nil)) - (when (re-search-forward ledger-amount-regex end t) - (goto-char (match-beginning 0)) - (skip-syntax-forward " ") - (cond - ((eq ledger-post-amount-alignment-at :end) - (- (or (match-end 4) (match-end 3)) (point))) - ((eq ledger-post-amount-alignment-at :decimal) - (- (match-end 3) (point))))))) - -(defun ledger-next-account (&optional end) - "Move to the beginning of the posting, or status marker. -Return the column of the beginning of the account and leave point -at beginning of account. -Looks only as far as END, if supplied, otherwise `point-max'." - (let ((end (or end (point-max)))) - (if (> end (point)) - (when (re-search-forward ledger-account-any-status-regex (1+ end) t) - ;; the 1+ is to make sure we can catch the newline - (if (match-beginning 1) - (goto-char (match-beginning 1)) - (goto-char (match-beginning 2))) - (current-column))))) - -(defun ledger-post-align-xact (pos) - "Align all the posting in the xact at POS." - (interactive "d") - (let ((bounds (ledger-navigate-find-xact-extents pos))) - (ledger-post-align-postings (car bounds) (cadr bounds)))) - -(defun ledger-post-align-postings (beg end) - "Align all accounts and amounts between BEG and END. -The current region is used, or, if no region, the current line." - (interactive "r") - (save-match-data - (save-excursion - (let ((inhibit-modification-hooks t) - ;; Extend region to whole lines - (beg (save-excursion (goto-char beg) (line-beginning-position))) - (end (save-excursion (goto-char end) (move-end-of-line 1) (point-marker)))) - (untabify beg end) - (goto-char beg) - (while (< (point) end) - (when (looking-at-p " ") - ;; fix spaces at beginning of line: - (just-one-space ledger-post-account-alignment-column) - ;; fix spaces before amount if any: - (when (re-search-forward "\t\\| \\| \t" (line-end-position) t) - (goto-char (match-beginning 0)) - (let ((acct-end-column (current-column)) - (amt-width (ledger-next-amount (line-end-position))) - amt-adjust) - (when amt-width - (if (/= 0 (setq amt-adjust (- (if (> (- ledger-post-amount-alignment-column amt-width) - (+ 2 acct-end-column)) - ledger-post-amount-alignment-column ;;we have room - (+ acct-end-column 2 amt-width)) - amt-width - (current-column)))) - (if (> amt-adjust 0) - (insert (make-string amt-adjust ? )) - (delete-char amt-adjust))))))) - (forward-line 1)))))) - -(defun ledger-indent-line () - "Indent the current line." - ;; Ensure indent if the previous line was indented - (let ((indent-level (save-excursion (if (and (zerop (forward-line -1)) - (memq (ledger-thing-at-point) '(transaction posting))) - ledger-post-account-alignment-column - 0)))) - (unless (= (current-indentation) indent-level) - (back-to-indentation) - (delete-horizontal-space t) - (indent-to indent-level))) - (when ledger-post-auto-align - (ledger-post-align-postings (line-beginning-position) (line-end-position)))) - -(defun ledger-post-align-dwim () - "Align all the posting of the current xact or the current region. - -If the point is in a comment, fill the comment paragraph as -regular text." - (interactive) - (cond - ((nth 4 (syntax-ppss)) - (call-interactively 'ledger-post-align-postings) - (fill-paragraph)) - ((use-region-p) (call-interactively 'ledger-post-align-postings)) - (t (call-interactively 'ledger-post-align-xact)))) - -(defun ledger-post-edit-amount () - "Call `calc' and push the amount in the posting to the top of stack, if any. - -In the calc buffer, press y to use the top value in the stack as -the amount and return to ledger." - (interactive) - (beginning-of-line) - (when (re-search-forward ledger-post-line-regexp (line-end-position) t) - (goto-char (match-end ledger-regex-post-line-group-account)) ;; go to the end of the account - ;; determine if there is an amount to edit - (if (re-search-forward ledger-amount-regexp (line-end-position) t) - (let ((val-string (match-string 0))) - (goto-char (match-beginning 0)) - (delete-region (match-beginning 0) (match-end 0)) - (push-mark (point) 'nomsg) - (calc) - ;; edit the amount, first removing thousands separators and converting - ;; decimal commas to calc's input format - (calc-eval (number-to-string (ledger-string-to-number val-string)) 'push) - (calc-renumber-stack)) - ;; make sure there are two spaces after the account name and go to calc - (if (search-backward " " (- (point) 3) t) - (end-of-line) - (insert " ")) - (push-mark (point) 'nomsg) - (calc)))) - -(provide 'ledger-post) - - - -;;; ledger-post.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-post.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-post.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-reconcile.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-reconcile.el @@ -1,700 +0,0 @@ -;;; ledger-reconcile.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - -;; Reconcile mode - - -;;; Commentary: -;; Code to handle reconciling Ledger files with outside sources - -;;; Code: - -(require 'easymenu) -(require 'ledger-init) - -(require 'ledger-xact) -(require 'ledger-occur) -(require 'ledger-commodities) -(require 'ledger-exec) -(require 'ledger-navigate) -(require 'ledger-state) -(declare-function ledger-insert-effective-date "ledger-mode" (&optional date)) -(declare-function ledger-read-account-with-prompt "ledger-mode" (prompt)) -(declare-function ledger-read-date "ledger-mode" (prompt)) - -(defvar-local ledger-reconcile-ledger-buf nil - "Buffer from which the current reconcile buffer was created.") - -(defvar-local ledger-reconcile-account nil - "Account being reconciled in the current buffer.") - -(defvar-local ledger-reconcile-target nil - "Target amount for this reconciliation process.") - -(defgroup ledger-reconcile nil - "Options for Ledger-mode reconciliation" - :group 'ledger) - -(define-obsolete-variable-alias - 'ledger-recon-buffer-name - 'ledger-reconcile-buffer-name - "2023-12-15") - -(defcustom ledger-reconcile-buffer-name "*Reconcile*" - "Name to use for reconciliation buffer." - :type 'string - :group 'ledger-reconcile) - -(defcustom ledger-narrow-on-reconcile t - "If t, show only transactions matching the reconcile regex in the main buffer." - :type 'boolean - :group 'ledger-reconcile) - -(defcustom ledger-buffer-tracks-reconcile-buffer t - "If t, move point in the ledger buffer when it moves in the reconcile buffer. -When the cursor is moved to a new transaction in the reconcile -buffer then that transaction will be shown in its source buffer." - :type 'boolean - :group 'ledger-reconcile) - -(defcustom ledger-reconcile-force-window-bottom nil - "If t, show the reconcile window below the register window and resize." - :type 'boolean - :group 'ledger-reconcile) - -(defcustom ledger-reconcile-toggle-to-pending t - "If t, then toggle between uncleared and pending. -reconcile-finish will mark all pending posting cleared." - :type 'boolean - :group 'ledger-reconcile) - -(defcustom ledger-reconcile-default-date-format ledger-default-date-format - "Date format for the reconcile buffer. -Default is `ledger-default-date-format'." - :type 'string - :group 'ledger-reconcile) - -(defcustom ledger-reconcile-target-prompt-string "Target amount for reconciliation " - "Prompt for reconcile target." - :type 'string - :group 'ledger-reconcile) - -(defcustom ledger-reconcile-buffer-header "Reconciling account %s\n\n" - "Default header string for the reconcile buffer. - -If non-nil, the name of the account being reconciled will be substituted - into the '%s'. If nil, no header will be displayed." - :type 'string - :group 'ledger-reconcile) - -(defcustom ledger-reconcile-buffer-line-format "%(date)s %-4(code)s %-50(payee)s %-30(account)s %15(amount)s\n" - "Format string for the ledger reconcile posting format. -Available fields are date, status, code, payee, account, -amount. The format for each field is %WIDTH(FIELD), WIDTH can be -preceded by a minus sign which mean to left justify and pad the -field. WIDTH is the minimum number of characters to display; -if string is longer, it is not truncated unless -`ledger-reconcile-buffer-payee-max-chars' or -`ledger-reconcile-buffer-account-max-chars' is defined." - :type 'string - :group 'ledger-reconcile) - -(defcustom ledger-reconcile-buffer-payee-max-chars -1 - "If positive, truncate payee name right side to max number of characters." - :type 'integer - :group 'ledger-reconcile) - -(defcustom ledger-reconcile-buffer-account-max-chars -1 - "If positive, truncate account name left side to max number of characters." - :type 'integer - :group 'ledger-reconcile) - -(defcustom ledger-reconcile-sort-key "(0)" - "Key for sorting reconcile buffer. - -Possible values are \"(date)\", \"(amount)\", \"(payee)\" or \"(0)\" for -no sorting, i.e. using ledger file order." - :type 'string - :group 'ledger-reconcile) - -(defcustom ledger-reconcile-insert-effective-date nil - "If t, prompt for effective date when clearing transactions. - -If this is a function, it is called with no arguments with point -at the posting to be cleared. The return value is then used as -described above." - :type '(choice boolean function) - :group 'ledger-reconcile) - -(defcustom ledger-reconcile-finish-force-quit nil - "If t, will force closing reconcile window after \\[ledger-reconcile-finish]." - :type 'boolean - :group 'ledger-reconcile) - -(defvar-local ledger-reconcile-last-balance-message nil) -(defvar-local ledger-reconcile-last-balance-equals-target nil) - -(defface ledger-reconcile-last-balance-equals-target-face - '((t :inherit header-line :foreground "green3")) - "Face used for header line when cleared-or-pending balance equals the target." - :group 'ledger-reconcile) - -;; s-functions below are copied from Magnars' s.el -;; prefix ledger-reconcile- is added to not conflict with s.el -(defun ledger-reconcile-s-pad-left (len padding s) - "If S is shorter than LEN, pad it with PADDING on the left." - (let ((extra (max 0 (- len (length s))))) - (concat (make-string extra (string-to-char padding)) - s))) -(defun ledger-reconcile-s-pad-right (len padding s) - "If S is shorter than LEN, pad it with PADDING on the right." - (let ((extra (max 0 (- len (length s))))) - (concat s - (make-string extra (string-to-char padding))))) -(defun ledger-reconcile-s-left (len s) - "Return up to the LEN first chars of S." - (if (> (length s) len) - (substring s 0 len) - s)) -(defun ledger-reconcile-s-right (len s) - "Return up to the LEN last chars of S." - (let ((l (length s))) - (if (> l len) - (substring s (- l len) l) - s))) - -(defun ledger-reconcile-truncate-right (str len) - "Truncate STR right side with max LEN characters, and pad with '…' if truncated." - (if (and (>= len 0) (> (length str) len)) - (ledger-reconcile-s-pad-right len "…" (ledger-reconcile-s-left (- len 1) str)) - str)) - -(defun ledger-reconcile-truncate-left (str len) - "Truncate STR left side with max LEN characters, and pad with '…' if truncated." - (if (and (>= len 0) (> (length str) len)) - (ledger-reconcile-s-pad-left len "…" (ledger-reconcile-s-right (- len 1) str)) - str)) - -(defun ledger-reconcile-get-cleared-or-pending-balance (buffer account) - "Use BUFFER to Calculate the cleared or pending balance of the ACCOUNT." - - ;; these vars are buffer local, need to hold them for use in the - ;; temp buffer below - - (with-temp-buffer - ;; note that in the line below, the --format option is - ;; separated from the actual format string. emacs does not - ;; split arguments like the shell does, so you need to - ;; specify the individual fields in the command line. - (ledger-exec-ledger buffer (current-buffer) - "balance" "--real" "--limit" "cleared or pending" "--empty" "--collapse" - "--format" "%(scrub(display_total))" account) - (ledger-split-commodity-string - (buffer-substring-no-properties (point-min) (point-max))))) - -(defun ledger-display-balance () - "Display the cleared-or-pending balance. -And calculate the target-delta of the account being reconciled." - (interactive) - (when-let (pending (ledger-reconcile-get-cleared-or-pending-balance ledger-reconcile-ledger-buf ledger-reconcile-account)) - (let ((message - (if-let (diff (and ledger-reconcile-target (ledger-subtract-commodity ledger-reconcile-target pending))) - (progn - (setq ledger-reconcile-last-balance-equals-target (zerop (car diff))) - (format-message "Cleared and Pending balance: %s, Difference from target: %s" - (ledger-commodity-to-string pending) - (ledger-commodity-to-string diff))) - (format-message "Pending balance: %s" - (ledger-commodity-to-string pending))))) - (setq ledger-reconcile-last-balance-message message) - (message "%s" message)))) - -(defun ledger-is-stdin (file) - "True if ledger FILE is standard input." - (or - (equal file "") - (equal file "<stdin>") - (equal file "/dev/stdin"))) - -(defun ledger-reconcile-get-buffer (where) - "Return a buffer from WHERE the transaction is." - (if (bufferp (car where)) - (car where) - (error "Function ledger-reconcile-get-buffer: Buffer not set"))) - -(defun ledger-reconcile-insert-effective-date () - "Prompt for an effective date and insert it at point, if enabled. - -If the value of variable `ledger-reconcile-insert-effective-date' -is a function, it is called with the point where the effective -date would be inserted. If it returns non-nil, prompt for an -effective date and insert it at point. If it is not a function, -do the same if its value is non-nil." - (when (if (functionp ledger-reconcile-insert-effective-date) - (save-excursion (funcall ledger-reconcile-insert-effective-date)) - ledger-reconcile-insert-effective-date) - (ledger-insert-effective-date))) - -(defun ledger-reconcile-toggle () - "Toggle the current transaction, and mark the reconcile window." - (interactive) - (beginning-of-line) - (let ((where (get-text-property (point) 'where)) - (inhibit-read-only t) - status) - (when (ledger-reconcile-get-buffer where) - (with-current-buffer (ledger-reconcile-get-buffer where) - (ledger-navigate-to-line (cdr where)) - (forward-char) - (setq status (ledger-toggle-current (if ledger-reconcile-toggle-to-pending - 'pending - 'cleared))) - ;; Ask for effective date & insert it, if enabled - (ledger-reconcile-insert-effective-date)) - ;; remove the existing face and add the new face - (remove-text-properties (line-beginning-position) - (line-end-position) - (list 'font-lock-face)) - (cond ((eq status 'pending) - (add-text-properties (line-beginning-position) - (line-end-position) - (list 'font-lock-face 'ledger-font-reconciler-pending-face ))) - ((eq status 'cleared) - (add-text-properties (line-beginning-position) - (line-end-position) - (list 'font-lock-face 'ledger-font-reconciler-cleared-face ))) - (t - (add-text-properties (line-beginning-position) - (line-end-position) - (list 'font-lock-face 'ledger-font-reconciler-uncleared-face ))))) - (forward-line) - (beginning-of-line) - (ledger-display-balance))) - -(defun ledger-reconcile-refresh () - "Force the reconciliation window to refresh. -Return the number of uncleared xacts found." - (interactive) - (let ((inhibit-read-only t) - (line (count-lines (point-min) (point)))) - (erase-buffer) - (prog1 - (ledger-do-reconcile ledger-reconcile-sort-key) - (set-buffer-modified-p t) - (ledger-reconcile-ensure-xacts-visible) - (ledger-display-balance) - (goto-char (point-min)) - (forward-line line)))) - -(defun ledger-reconcile-refresh-after-save () - "Refresh the reconcile window after the ledger buffer is saved." - (let ((curbufwin (get-buffer-window (current-buffer))) - (curpoint (point)) - (reconcile-buf (get-buffer ledger-reconcile-buffer-name))) - (when (buffer-live-p reconcile-buf) - (with-current-buffer reconcile-buf - (ledger-reconcile-refresh) - (set-buffer-modified-p nil)) - (when curbufwin - (select-window curbufwin) - (goto-char curpoint) - (recenter) - (ledger-highlight-xact-under-point))))) - -(defun ledger-reconcile-add (date xact) - "Use ledger xact to add a new transaction. - -When called interactively, prompt for DATE, then XACT." - (interactive - (list (ledger-read-date "Date: ") - (read-string "Transaction: " nil 'ledger-minibuffer-history))) - (with-current-buffer ledger-reconcile-ledger-buf - (ledger-add-transaction (concat date " " xact))) - (ledger-reconcile-refresh)) - -(defun ledger-reconcile-delete () - "Delete the transactions pointed to in the reconcile window." - (interactive) - (let ((where (get-text-property (point) 'where))) - (when (ledger-reconcile-get-buffer where) - (with-current-buffer (ledger-reconcile-get-buffer where) - (ledger-navigate-to-line (cdr where)) - (ledger-delete-current-transaction (point))) - (let ((inhibit-read-only t)) - (delete-region (line-beginning-position) - (min (1+ (line-end-position)) (point-max))) - (set-buffer-modified-p t)) - (ledger-reconcile-refresh) - (ledger-reconcile-visit t)))) - -(defun ledger-reconcile-visit (&optional come-back) - "Recenter ledger buffer on transaction and COME-BACK if non-nil." - (interactive) - (beginning-of-line) - (let* ((where (get-text-property (1+ (point)) 'where)) - (target-buffer (if where - (ledger-reconcile-get-buffer where) - nil)) - (cur-win (get-buffer-window (get-buffer ledger-reconcile-buffer-name)))) - (when target-buffer - (switch-to-buffer-other-window target-buffer) - (ledger-navigate-to-line (cdr where)) - (forward-char) - (recenter) - (ledger-highlight-xact-under-point) - (forward-char -1) - (when (and come-back cur-win) - (select-window cur-win) - (get-buffer ledger-reconcile-buffer-name))))) - - -(defun ledger-reconcile-save () - "Save the ledger buffer." - (interactive) - (with-selected-window (selected-window) ; restoring window is needed because after-save-hook will modify window and buffers - (with-current-buffer ledger-reconcile-ledger-buf - (basic-save-buffer)))) - - -(defun ledger-reconcile-finish () - "Mark all pending posting or transactions as cleared. -Depends on ledger-clear-whole-transactions, save the buffers and -exit reconcile mode if `ledger-reconcile-finish-force-quit'" - (interactive) - (save-excursion - (goto-char (point-min)) - (while (not (eobp)) - (let ((where (get-text-property (point) 'where)) - (face (get-text-property (point) 'font-lock-face))) - (if (eq face 'ledger-font-reconciler-pending-face) - (with-current-buffer (ledger-reconcile-get-buffer where) - (ledger-navigate-to-line (cdr where)) - (ledger-toggle-current 'cleared)))) - (forward-line 1))) - (ledger-reconcile-save) - (when ledger-reconcile-finish-force-quit - (ledger-reconcile-quit))) - - -(defun ledger-reconcile-quit () - "Quit the reconcile window without saving ledger buffer." - (interactive) - (let ((reconcile-buf (get-buffer ledger-reconcile-buffer-name)) - buf) - (if reconcile-buf - (with-current-buffer reconcile-buf - (ledger-reconcile-quit-cleanup) - (setq buf ledger-reconcile-ledger-buf) - ;; Make sure you delete the window before you delete the buffer, - ;; otherwise, madness ensues - (delete-window (get-buffer-window reconcile-buf)) - (kill-buffer reconcile-buf) - (set-window-buffer (selected-window) buf))))) - -(defun ledger-reconcile-quit-cleanup () - "Cleanup all hooks established by reconcile mode." - (interactive) - (let ((buf ledger-reconcile-ledger-buf)) - (if (buffer-live-p buf) - (with-current-buffer buf - (remove-hook 'after-save-hook 'ledger-reconcile-refresh-after-save t) - (when ledger-narrow-on-reconcile - (ledger-occur-mode -1) - (ledger-highlight-xact-under-point)))))) - -(defun ledger-marker-where-xact-is (emacs-xact posting) - "Find the position of the EMACS-XACT in the `ledger-reconcile-ledger-buf'. -POSTING is used in `ledger-clear-whole-transactions' is nil." - (let ((buf (if (ledger-is-stdin (nth 0 emacs-xact)) - ledger-reconcile-ledger-buf - (find-file-noselect (nth 0 emacs-xact))))) - (cons - buf - (if (or ledger-clear-whole-transactions - ;; The posting might not be part of the ledger buffer. This can - ;; happen if the account to reconcile is the default account. In - ;; that case, we just behave as if ledger-clear-whole-transactions - ;; was turned on. See #58 for more info. - (= -1 (nth 0 posting))) - (nth 1 emacs-xact) ;; return line-no of xact - (nth 0 posting))))) ;; return line-no of posting - -(defun ledger-reconcile-compile-format-string (fstr) - "Return a function that implements the format string in FSTR." - (let (fields - (start 0)) - (while (string-match "(\\(.*?\\))" fstr start) - (setq fields (cons (intern (match-string 1 fstr)) fields)) - (setq start (match-end 0))) - (setq fields (cl-list* 'format (replace-regexp-in-string "(.*?)" "" fstr) (nreverse fields))) - `(lambda (date code status payee account amount) - ,fields))) - - - -(defun ledger-reconcile-format-posting (beg where fmt date code status payee account amount) - "Format posting for the reconcile buffer." - (insert (funcall fmt date code status payee account amount)) - - ; Set face depending on cleared status - (if status - (if (eq status 'pending) - (set-text-properties beg (1- (point)) - (list 'font-lock-face 'ledger-font-reconciler-pending-face - 'where where)) - (set-text-properties beg (1- (point)) - (list 'font-lock-face 'ledger-font-reconciler-cleared-face - 'where where))) - (set-text-properties beg (1- (point)) - (list 'font-lock-face 'ledger-font-reconciler-uncleared-face - 'where where)))) - -(defun ledger-reconcile-format-xact (xact fmt) - "Format XACT using FMT." - (dolist (posting (nthcdr 5 xact)) - (let ((beg (point)) - (where (ledger-marker-where-xact-is xact posting))) - (ledger-reconcile-format-posting beg - where - fmt - (ledger-format-date (nth 2 xact)) ; date - (if (nth 3 xact) (nth 3 xact) "") ; code - (nth 3 posting) ; status - (ledger-reconcile-truncate-right - (nth 4 xact) ; payee - ledger-reconcile-buffer-payee-max-chars) - (ledger-reconcile-truncate-left - (nth 1 posting) ; account - ledger-reconcile-buffer-account-max-chars) - (nth 2 posting))))) ; amount - -(defun ledger-do-reconcile (&optional sort) - "SORT the uncleared transactions in the account. -The sorted results are displayed in in the *Reconcile* buffer. -Return a count of the uncleared transactions." - (let* ((buf ledger-reconcile-ledger-buf) - (account ledger-reconcile-account) - (sort-by (if sort - sort - "(date)")) - (xacts - (with-temp-buffer - (ledger-exec-ledger buf (current-buffer) - "--uncleared" "--real" "emacs" "--sort" sort-by account) - (goto-char (point-min)) - (when (and (not (eobp)) (looking-at "(")) - (read (current-buffer))))) - (fmt (ledger-reconcile-compile-format-string ledger-reconcile-buffer-line-format))) - (if (null xacts) - (insert (concat "There are no uncleared entries for " account)) - (if ledger-reconcile-buffer-header - (insert (format ledger-reconcile-buffer-header account))) - (dolist (xact xacts) - (ledger-reconcile-format-xact xact fmt)) - (goto-char (point-max)) - (delete-char -1)) ;gets rid of the extra line feed at the bottom of the list - (goto-char (point-min)) - (set-buffer-modified-p nil) - (setq buffer-read-only t) - - (length xacts))) - -(defun ledger-reconcile-ensure-xacts-visible () - "Ensure the last of the visible transactions in the ledger buffer is visible. -This is achieved by placing that transaction at the bottom of the main window. -The key to this is to ensure the window is selected when the buffer point is -moved and recentered. If they aren't strange things happen." - (let ((reconcile-window (get-buffer-window (get-buffer ledger-reconcile-buffer-name)))) - (when reconcile-window - (fit-window-to-buffer reconcile-window) - (with-current-buffer ledger-reconcile-ledger-buf - (add-hook 'kill-buffer-hook 'ledger-reconcile-quit nil t) - (if (get-buffer-window ledger-reconcile-ledger-buf) - (select-window (get-buffer-window ledger-reconcile-ledger-buf))) - (recenter)) - (select-window reconcile-window) - (ledger-reconcile-visit t)) - (with-current-buffer ledger-reconcile-ledger-buf - (when ledger-occur-mode - (ledger-occur-refresh))) - (add-hook 'post-command-hook 'ledger-reconcile-track-xact nil t))) - -(defun ledger-reconcile-track-xact () - "Recenter the ledger buffer on the transaction at point in the reconcile buffer." - (if (and ledger-buffer-tracks-reconcile-buffer - (member this-command (list 'next-line - 'previous-line - 'mouse-set-point - 'ledger-reconcile-toggle - 'end-of-buffer - 'beginning-of-buffer))) - (save-excursion - (ledger-reconcile-visit t)))) - -(defun ledger-reconcile-open-windows (buf rbuf) - "Ensure that the ledger buffer BUF is split by RBUF." - (if ledger-reconcile-force-window-bottom - ;;create the *Reconcile* window directly below the ledger buffer. - (set-window-buffer (split-window (get-buffer-window buf) nil nil) rbuf) - (pop-to-buffer rbuf))) - -(defun ledger-reconcile-check-valid-account (account) - "Check to see if ACCOUNT exists in the ledger file." - (if (> (length account) 0) - (save-excursion - (goto-char (point-min)) - (search-forward account nil t)))) - -(defun ledger-reconcile (&optional account target) - "Start reconciling, prompt for ACCOUNT. - -If TARGET is non-nil, it is used as the initial target for -reconciliation, otherwise prompt for TARGET." - (interactive) - (let ((account (or account (ledger-read-account-with-prompt "Account to reconcile"))) - (buf (current-buffer)) - (rbuf (get-buffer ledger-reconcile-buffer-name))) - - (when (ledger-reconcile-check-valid-account account) - (if rbuf ;; *Reconcile* already exists - (with-current-buffer rbuf - (setq ledger-reconcile-account account) - (when (not (eq buf rbuf)) - ;; called from some other ledger-mode buffer - (ledger-reconcile-quit-cleanup) - (setq ledger-reconcile-ledger-buf buf)) - - (unless (get-buffer-window rbuf) - (ledger-reconcile-open-windows buf rbuf))) - - ;; no reconcile-buffer, starting from scratch. - - (with-current-buffer (setq rbuf - (get-buffer-create ledger-reconcile-buffer-name)) - (ledger-reconcile-open-windows buf rbuf) - (ledger-reconcile-mode) - (setq ledger-reconcile-ledger-buf buf) - (setq ledger-reconcile-account account))) - - (add-hook 'after-save-hook 'ledger-reconcile-refresh-after-save nil t) - - ;; Narrow the ledger buffer - (if ledger-narrow-on-reconcile - (ledger-occur (regexp-quote account))) - - (setq ledger-reconcile-last-balance-message nil) - (setq ledger-reconcile-last-balance-equals-target nil) - - (with-current-buffer rbuf - (if (> (ledger-reconcile-refresh) 0) - (ledger-reconcile-change-target target) - (ledger-display-balance)))))) - -(defvar ledger-reconcile-mode-abbrev-table) - -(defun ledger-reconcile-change-target (&optional target) - "Change the TARGET amount for the reconciliation process." - (interactive) - (setq ledger-reconcile-target (or target (ledger-read-commodity-string ledger-reconcile-target-prompt-string))) - (ledger-display-balance)) - -(defmacro ledger-reconcile-change-sort-key-and-refresh (sort-by) - "Set the sort-key to SORT-BY." - `(lambda () - (interactive) - - (setq ledger-reconcile-sort-key ,sort-by) - (ledger-reconcile-refresh))) - -(defvar ledger-reconcile-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-m") #'ledger-reconcile-visit) - (define-key map (kbd "<return>") #'ledger-reconcile-visit) - (define-key map (kbd "C-x C-s") #'ledger-reconcile-save) - (define-key map (kbd "C-l") #'ledger-reconcile-refresh) - (define-key map (kbd "C-c C-c") #'ledger-reconcile-finish) - (define-key map (kbd "SPC") #'ledger-reconcile-toggle) - (define-key map (kbd "a") #'ledger-reconcile-add) - (define-key map (kbd "d") #'ledger-reconcile-delete) - (define-key map (kbd "g") #'ledger-reconcile); - (define-key map (kbd "n") #'next-line) - (define-key map (kbd "p") #'previous-line) - (define-key map (kbd "t") #'ledger-reconcile-change-target) - (define-key map (kbd "s") #'ledger-reconcile-save) - (define-key map (kbd "q") #'ledger-reconcile-quit) - (define-key map (kbd "b") #'ledger-display-balance) - (define-key map (kbd "B") #'ledger-reconcile-display-balance-in-header-mode) - - (define-key map (kbd "C-c C-o") (ledger-reconcile-change-sort-key-and-refresh "(0)")) - - (define-key map (kbd "C-c C-a") (ledger-reconcile-change-sort-key-and-refresh "(amount)")) - - (define-key map (kbd "C-c C-d") (ledger-reconcile-change-sort-key-and-refresh "(date)")) - - (define-key map (kbd "C-c C-p") (ledger-reconcile-change-sort-key-and-refresh "(payee)")) - map) - "Keymap for `ledger-reconcile-mode'.") - -(easy-menu-define ledger-reconcile-mode-menu ledger-reconcile-mode-map - "Ledger reconcile menu" - `("Reconcile" - ["Save" ledger-reconcile-save] - ["Refresh" ledger-reconcile-refresh] - ["Finish" ledger-reconcile-finish] - "---" - ["Reconcile New Account" ledger-reconcile] - "---" - ["Change Target Balance" ledger-reconcile-change-target] - ["Show Cleared Balance" ledger-display-balance] - "---" - ["Sort by payee" ,(ledger-reconcile-change-sort-key-and-refresh "(payee)")] - ["Sort by date" ,(ledger-reconcile-change-sort-key-and-refresh "(date)")] - ["Sort by amount" ,(ledger-reconcile-change-sort-key-and-refresh "(amount)")] - ["Sort by file order" ,(ledger-reconcile-change-sort-key-and-refresh "(0)")] - "---" - ["Toggle Entry" ledger-reconcile-toggle] - ["Add Entry" ledger-reconcile-add] - ["Delete Entry" ledger-reconcile-delete] - "---" - ["Next Entry" next-line] - ["Visit Source" ledger-reconcile-visit] - ["Previous Entry" previous-line] - "---" - ["Quit" ledger-reconcile-quit] - )) - -(define-derived-mode ledger-reconcile-mode text-mode "Reconcile" - "A mode for reconciling ledger entries.") - -(define-minor-mode ledger-reconcile-display-balance-in-header-mode - "When enabled, display the cleared-or-pending balance in the header." - :group 'ledger-reconcile - (if ledger-reconcile-display-balance-in-header-mode - (setq header-line-format '(ledger-reconcile-last-balance-equals-target - (:propertize - ledger-reconcile-last-balance-message - face ledger-reconcile-last-balance-equals-target-face) - ledger-reconcile-last-balance-message)) - (setq header-line-format nil))) - -(provide 'ledger-reconcile) - -;;; ledger-reconcile.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-reconcile.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-reconcile.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-regex.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-regex.el @@ -1,428 +0,0 @@ -;;; ledger-regex.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - - -;;; Commentary: -;; Regular expressions used by ledger-mode. - -;;; Code: - -(require 'rx) -(require 'cl-lib) - -(defvar ledger-iso-date-regexp) - -(defconst ledger-amount-decimal-comma-regex - "-?\\(?:[1-9][0-9.]\\|0\\)*[,]?[0-9]*") - -(defconst ledger-amount-decimal-period-regex - "-?\\(?:[1-9][0-9,]*\\|0\\)[.]?[0-9]*") - -(defconst ledger-other-entries-regex - "\\(^[~=A-Za-z].+\\)+") - -(defconst ledger-comment-regex - "^[;#|\\*%].*\\|[ \t]+;.*") - -(defconst ledger-multiline-comment-start-regex - "^!comment$") -(defconst ledger-multiline-comment-end-regex - "^!end_comment$") -(defconst ledger-multiline-comment-regex - "^!comment\n\\(.*\n\\)*?!end_comment$") - -(defconst ledger-payee-any-status-regex - "^[0-9]+[-/][-/.=0-9]+\\(?:\\s-+\\*\\)?\\(?:\\s-+(.*?)\\)?\\s-+\\(.+?\\)\\s-*\\(?:;\\|$\\)") - -(defconst ledger-payee-pending-regex - "^[0-9]+[-/][-/.=0-9]+\\s-!\\s-+\\(([^)]+)\\s-+\\)?\\([^*].+?\\)\\s-*\\(;\\|$\\)") - -(defconst ledger-payee-cleared-regex - "^[0-9]+[-/][-/.=0-9]+\\s-\\*\\s-+\\(([^)]+)\\s-+\\)?\\([^*].+?\\)\\s-*\\(;\\|$\\)") - -(defconst ledger-payee-uncleared-regex - "^[0-9]+[-/][-/.=0-9]+\\s-+\\(([^)]+)\\s-+\\)?\\([^*].+?\\)\\s-*\\(;\\|$\\)") - -(defconst ledger-payee-directive-regex - (concat "^payee[ \t]+\\(.*?\\)[ \t]*$")) - -(defconst ledger-payee-name-or-directive-regex - (format "\\(?:%s\\|%s\\)" ledger-payee-any-status-regex ledger-payee-directive-regex)) - -(defconst ledger-init-string-regex - "^--.+?\\($\\|[ ]\\)") - -(defconst ledger-account-name-regex - "\\(?1:[^][(); \t\r\n]+\\(?: [^][(); \t\r\n]+\\)*\\)") - -(defconst ledger-account-directive-regex - (concat "^account[ \t]+" ledger-account-name-regex)) - -(defconst ledger-account-name-maybe-virtual-regex - (concat "[[(]?" ledger-account-name-regex "[])]?")) - -(defconst ledger-account-any-status-regex - (concat "^[ \t]+\\(?:[!*][ \t]*\\)?" ledger-account-name-maybe-virtual-regex)) - -;; This would incorrectly match "account (foo)", but writing the regexp this way -;; allows us to have just one match result -(defconst ledger-account-name-or-directive-regex - (format "\\(?:%s\\|%s\\)" ledger-account-any-status-regex ledger-account-directive-regex)) - -(defconst ledger-account-pending-regex - (concat "\\(^[ \t]+\\)!" ledger-account-name-maybe-virtual-regex)) - -(defconst ledger-account-cleared-regex - (concat "\\(^[ \t]+\\)*" ledger-account-name-maybe-virtual-regex)) - -(defmacro ledger-define-regexp (name regex docs &rest args) - "Simplify the creation of a Ledger regex and helper functions." - (let* ((regex (eval regex)) - (group-count (regexp-opt-depth regex)) - (defs - (list - `(defconst - ,(intern (concat "ledger-" (symbol-name name) "-regexp")) - ,regex - ,docs) - `(defconst - ,(intern (concat "ledger-regex-" (symbol-name name) - "-group--count")) - ,group-count))) - (addend 0) last-group) - (if (null args) - (progn - (when (cl-plusp group-count) - (nconc - defs - (list - `(defconst - ,(intern - (concat "ledger-regex-" (symbol-name name) "-group")) - 1))) - (nconc - defs - (list - `(defmacro - ,(intern (concat "ledger-regex-" (symbol-name name))) - (&optional string) - ,(format "Return the match string for the %s" name) - (match-string - ,(intern (concat "ledger-regex-" (symbol-name name) - "-group")) - string)))))) - - (while args - (let (arg var grouping target force-increment) - (setq arg (pop args)) - - (when (eq arg :separate) - (setq arg (pop args)) - (setq force-increment t)) - - (if (symbolp arg) - (setq var arg target arg) - (cl-assert (listp arg)) - (if (= 2 (length arg)) - (setq var (car arg) - target (cadr arg)) - (setq var (car arg) - grouping (cadr arg) - target (cl-caddr arg)))) - - (if (and last-group - (or (not (eq last-group (or grouping target))) - force-increment)) - (cl-incf addend - (symbol-value - (intern-soft (concat "ledger-regex-" - (symbol-name last-group) - "-group--count"))))) - (nconc - defs - (list - `(defconst - ,(intern (concat "ledger-regex-" (symbol-name name) - "-group-" (symbol-name var))) - ,(+ addend - (symbol-value - (intern-soft - (if grouping - (concat "ledger-regex-" (symbol-name grouping) - "-group-" (symbol-name target)) - (concat "ledger-regex-" (symbol-name target) - "-group")))))))) - (nconc - defs - (list - `(defmacro - ,(intern (concat "ledger-regex-" (symbol-name name) - "-" (symbol-name var))) - (&optional string) - ,(format "Return the sub-group match for the %s %s." - name var) - (match-string - ,(intern (concat "ledger-regex-" (symbol-name name) - "-group-" (symbol-name var))) - string)))) - - (setq last-group (or grouping target))))) - - (cons 'eval-and-compile defs))) - -(put 'ledger-define-regexp 'lisp-indent-function 1) - -(ledger-define-regexp iso-date - (let ((sep '(or ?- ?/))) - (rx (group - (and (group (= 4 num)) - (eval sep) - (group (and num (? num))) - (eval sep) - (group (and num (? num))))))) - "Match a single date, in its \"written\" form.") - -(ledger-define-regexp full-date - (macroexpand - `(rx (and (regexp ,ledger-iso-date-regexp) - (? (and ?= (regexp ,ledger-iso-date-regexp)))))) - "Match a compound date, of the form ACTUAL=EFFECTIVE" - (actual iso-date) - :separate - (effective iso-date)) - -(ledger-define-regexp state - (rx (group (any ?! ?*))) - "Match a transaction or posting's \"state\" character.") - -(ledger-define-regexp code - (rx (and ?\( (group (+? (not (any ?\))))) ?\))) - "Match the transaction code.") - -(ledger-define-regexp long-space - (rx (and (*? blank) - (or (and ? (or ? ?\t)) ?\t))) - "Match a \"long space\".") - -(ledger-define-regexp note - (rx (group (+ nonl))) - "") - -(ledger-define-regexp end-note - (macroexpand - `(rx (and (regexp ,ledger-long-space-regexp) ?\; - (regexp ,ledger-note-regexp)))) - "") - -(ledger-define-regexp full-note - (macroexpand - `(rx (and line-start (+ blank) - ?\; (regexp ,ledger-note-regexp)))) - "") - -(ledger-define-regexp xact-line - (macroexpand - `(rx (and line-start - (regexp ,ledger-full-date-regexp) - (? (and (+ blank) (regexp ,ledger-state-regexp))) - (? (and (+ blank) (regexp ,ledger-code-regexp))) - (+ blank) (+? nonl) - (? (regexp ,ledger-end-note-regexp)) - line-end))) - "Match a transaction's first line (and optional notes)." - (actual-date full-date actual) - (effective-date full-date effective) - state - code - (note end-note)) - -(ledger-define-regexp account - (rx (group (and (not (any blank ?\[ ?\( ?: ?\;)) (*? nonl)))) - "") - -(ledger-define-regexp account-kind - (rx (group (? (any ?\[ ?\()))) - "") - -(ledger-define-regexp full-account - (macroexpand - `(rx (and (regexp ,ledger-account-kind-regexp) - (regexp ,ledger-account-regexp) - (? (any ?\] ?\)))))) - "" - (kind account-kind) - (name account)) - -(ledger-define-regexp commodity-no-group - (rx (or (and ?\" (+ (not (any ?\"))) ?\") - (+ (not (any blank ?\n - digit - ?- ?\[ ?\] - ?. ?, ?\; ?+ ?* ?/ ?^ ?? ?: ?& ?| ?! ?= - ?\< ?\> ?\{ ?\} ?\( ?\) ?@))))) - "") - -(ledger-define-regexp commodity - (macroexpand - `(rx (group (regexp ,ledger-commodity-no-group-regexp)))) - "") - -(ledger-define-regexp amount-no-group - (rx (and (? ?-) - (+ digit) - (* (and (any ?. ?,) (+ digit))))) - "") - -(ledger-define-regexp amount - (macroexpand - `(rx (group (regexp ,ledger-amount-no-group-regexp)))) - "") - -(ledger-define-regexp commoditized-amount - (macroexpand - `(rx (group - (or (and (regexp ,ledger-commodity-no-group-regexp) - (*? blank) - (regexp ,ledger-amount-no-group-regexp)) - (and (regexp ,ledger-amount-no-group-regexp) - (*? blank) - (regexp ,ledger-commodity-no-group-regexp)))))) - "") - -(ledger-define-regexp commodity-annotations - (macroexpand - `(rx (* (+ blank) - (or (and ?\{ (regexp ,ledger-commoditized-amount-regexp) ?\}) - (and ?\[ (regexp ,ledger-iso-date-regexp) ?\]) - (and ?\( (not (any ?\))) ?\)))))) - "" - commoditized-amount - iso-date) - -(ledger-define-regexp cost - (macroexpand - `(rx (and (or "@" "@@") (+ blank) - (regexp ,ledger-commoditized-amount-regexp)))) - "") - -(ledger-define-regexp balance-assertion - (macroexpand - `(rx (and ?= (+ blank) - (regexp ,ledger-commoditized-amount-regexp)))) - "") - -(ledger-define-regexp full-amount - (rx (group (+? (not (any ?\;))))) - "") - -(ledger-define-regexp post-line - (macroexpand - `(rx (and line-start (+ blank) - (? (and (regexp ,ledger-state-regexp) (* blank))) - (regexp ,ledger-full-account-regexp) - (? (and (regexp ,ledger-long-space-regexp) - (regexp ,ledger-full-amount-regexp))) - (? (regexp ,ledger-end-note-regexp)) - line-end))) - "" - state - (account-kind full-account kind) - (account full-account name) - (amount full-amount) - (note end-note)) - -(defconst ledger-amount-regex - (concat "\\( \\|\t\\| \t\\)[ \t]*[-+=]? *" - "\\(?:" ledger-commodity-regexp " *\\)?" - ;; We either match just a number after the commodity with no - ;; decimal or thousand separators or a number with thousand - ;; separators. If we have a decimal part starting with `,' - ;; or `.', because the match is non-greedy, it must leave at - ;; least one of those symbols for the following capture - ;; group, which then finishes the decimal part. - "\\([-+=]? *\\(?:[0-9]+\\|[0-9,.]+?\\)\\)" - "\\([,.][0-9)]+\\)?" - "\\(?: *" ledger-commodity-regexp "\\)?" - "\\([ \t]*[@={]@?[^\n;]+?\\)?" - "\\([ \t]+;.+?\\|[ \t]*\\)?$")) - -(ledger-define-regexp year - (rx (group (+ (any "0-9")))) - "") - -(ledger-define-regexp payee - (rx (group (+? nonl))) - "") - -(ledger-define-regexp iterate - (macroexpand `(rx (or (and (or "Y" "year") - (+ (syntax ?-)) - (regexp ,ledger-year-regexp)) - (and (regexp ,ledger-full-date-regexp) - (? (and (+ blank) (regexp ,ledger-state-regexp))) - (? (and (+ blank) (regexp ,ledger-code-regexp))) - (+ blank) - (regexp ,ledger-payee-regexp) - (? (regexp ,ledger-end-note-regexp)))))) - "" - year - (actual-date full-date actual) - (effective-date full-date effective) - state - code - payee - (note end-note)) - -(defconst ledger-incomplete-date-regexp - "\\(?:\\([0-9]\\{1,2\\}\\)[-/]\\)?\\([0-9]\\{1,2\\}\\)") - -(defconst ledger-xact-start-regex - (concat "^" ledger-iso-date-regexp ;; subexp 1 - "\\(=" ledger-iso-date-regexp "\\)?" - )) - -(defconst ledger-xact-after-date-regex - (concat "\\(?:[ \t]+\\([*!]\\)\\)?" ;; mark, subexp 1 - "\\(?:[ \t]+\\((.*?)\\)\\)?" ;; code, subexp 2 - "\\(?:[ \t]+\\(.+?\\)\\)?" ;; desc, subexp 3 - "\\(?:\\(?:\t\\|[ \t]\\{2,\\}\\)\\(;[^\n]*\\)\\)?$" ;; comment, subexp 4 - )) - -(defconst ledger-posting-regex - (concat "^[[:blank:]]+" ; initial white space - ;; state and account, subexp 1 - "\\(\\([*!]\\)?" ; state, subexp 2 - "[[:blank:]]*\\(.*?\\)\\)?" ; account, subexp 3 - "\\(?:\\(?:\t\\|[[:blank:]]\\{2,\\}\\)" - "\\([^;\n]*?\\)\\)?" ; amount, subexp 4 - "\\(?:\\(?:\t\\|[[:blank:]]\\{2,\\}\\)" - "\\(;.*\\)\\)?$" ; comment, subexp 5 - )) - - - -(defconst ledger-directive-start-regex - "[=~;#%|\\*[A-Za-z]") - - -(provide 'ledger-regex) - -;;; ledger-regex.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-regex.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-regex.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-report.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-report.el @@ -1,668 +0,0 @@ -;;; ledger-report.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - - -;;; Commentary: -;; Provide facilities for running and saving reports in Emacs - -;;; Code: - -(require 'ledger-xact) -(require 'ledger-navigate) -(require 'ledger-commodities) -(declare-function ledger-read-string-with-default "ledger-mode" (prompt default)) -(declare-function ledger-read-account-with-prompt "ledger-mode" (prompt)) - -(require 'easymenu) -(require 'ansi-color) -(require 'font-lock) -(eval-when-compile - (require 'rx) - (require 'subr-x)) - -(defgroup ledger-report nil - "Customization option for the Report buffer." - :group 'ledger) - -(defcustom ledger-reports - '(("bal" "%(binary) -f %(ledger-file) bal") - ("reg" "%(binary) -f %(ledger-file) reg") - ("payee" "%(binary) -f %(ledger-file) reg @%(payee)") - ("account" "%(binary) -f %(ledger-file) reg %(account)")) - "Definition of reports to run. - -Each element has the form (NAME CMDLINE). The command line can -contain format specifiers that are replaced with context sensitive -information. Format specifiers have the format '%(<name>)' where -<name> is an identifier for the information to be replaced. The -`ledger-report-format-specifiers' alist variable contains a mapping -from format specifier identifier to a Lisp function that implements -the substitution. See the documentation of the individual functions -in that variable for more information on the behavior of each -specifier." - :type '(repeat (list (string :tag "Report Name") - (string :tag "Command Line"))) - :group 'ledger-report) - -(defcustom ledger-report-format-specifiers - '(("ledger-file" . ledger-report-ledger-file-format-specifier) - ("binary" . ledger-report-binary-format-specifier) - ("payee" . ledger-report-payee-format-specifier) - ("account" . ledger-report-account-format-specifier) - ("month" . ledger-report-month-format-specifier) - ("tagname" . ledger-report-tagname-format-specifier) - ("tagvalue" . ledger-report-tagvalue-format-specifier)) - "An alist mapping ledger report format specifiers to implementing functions. - -The function is called with no parameters and expected to return -a string, or a list of strings, that should replace the format specifier. -Single strings are quoted with `shell-quote-argument'; lists of strings are -simply concatenated (no quoting)." - :type 'alist - :group 'ledger-report) - -(defcustom ledger-report-auto-refresh t - "If non-nil, automatically rerun the report when the ledger buffer is saved." - :type 'boolean - :group 'ledger-report) - -(defcustom ledger-report-auto-refresh-sticky-cursor nil - "If non-nil, keep cursor's relative position after auto-refresh." - :type 'boolean - :group 'ledger-report) - -(defcustom ledger-report-links-in-register t - "If non-nil, link entries in \"register\" reports to entries in the ledger buffer." - :type 'boolean - :group 'ledger-report) - -(defcustom ledger-report-use-native-highlighting t - "When non-nil, use ledger's native highlighting in reports." - :type 'boolean - :package-version '(ledger-mode . "4.0.0") - :group 'ledger-report) - -(defcustom ledger-report-native-highlighting-arguments '("--color" "--force-color") - "List of ledger args needed by `ledger-report-use-native-highlighting'. - -If you are using hledger instead of ledger, you might want to set -this variable to `(\"--color=always\")'." - :type '(repeat string) - :group 'ledger-report) - -(defcustom ledger-report-auto-width t - "When non-nil, tell ledger about the width of the report window." - :type 'boolean - :package-version '(ledger-mode . "4.0.0") - :group 'ledger-report) - -(defcustom ledger-report-use-header-line nil - "If non-nil, indicate report name/command in the `header-line'. -The report name/command won't be printed in the buffer. See -`ledger-report-header-line-fn' for how to customize the -information reported." - :type 'boolean - :package-version '(ledger-mode . "4.0.0") - :group 'ledger-report) - -(defcustom ledger-report-header-line-fn #'ledger-report--header-function - "Evaluate this function in the `header-line' of the report buffer. -`ledger-report-use-header-line' must be non-nil for this to have any effect." - :type 'function - :package-version '(ledger-mode . "4.0.0") - :group 'ledger-report) - -(defcustom ledger-report-resize-window t - "If non-nil, resize the report window. -Calls `shrink-window-if-larger-than-buffer'." - :type 'boolean - :package-version '(ledger-mode . "4.0.0") - :group 'ledger-report) - -(defcustom ledger-report-use-strict nil - "When non-nil, `ledger-mode' will use --strict when running reports?" - :type 'boolean - :package-version '(ledger-mode . "4.0.0") - :group 'ledger-report) - -(defcustom ledger-report-after-report-hook nil - "Hook run after `ledger-report' has created the buffer and report." - :type 'boolean - :package-version '(ledger-mode . "4.0.0") - :group 'ledger-report) - -(defvar ledger-report-buffer-name "*Ledger Report*") - -(defvar-local ledger-report-name nil) -(defvar-local ledger-report-cmd nil) -(defvar-local ledger-report-saved nil) -(defvar-local ledger-report-current-month nil) -(defvar-local ledger-report-is-reversed nil) -(defvar-local ledger-report-cursor-line-number nil) -(defvar-local ledger-report-ledger-buf nil) -(defvar-local ledger-master-file nil - "The master file for the current buffer. -See documentation for the function `ledger-master-file'") - -(defvar ledger-report-name-prompt-history nil) -(defvar ledger-report-cmd-prompt-history nil) -(defvar ledger-minibuffer-history nil) -(defvar ledger-report-mode-abbrev-table) - -(defun ledger-report-reverse-report () - "Reverse the order of the report." - (interactive) - (ledger-report-reverse-lines) - (setq ledger-report-is-reversed (not ledger-report-is-reversed))) - -(defun ledger-report-reverse-lines () - "Reverse the lines in the ledger report buffer." - (with-silent-modifications - (goto-char (point-min)) - (unless ledger-report-use-header-line - (forward-paragraph) - (forward-line)) - (save-excursion - (reverse-region (point) (point-max))))) - -(defun ledger-report-maybe-shrink-window () - "Shrink window if `ledger-report-resize-window' is non-nil." - (when ledger-report-resize-window - (shrink-window-if-larger-than-buffer))) - -(defvar ledger-report-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "r") #'ledger-report-redo) - (define-key map (kbd "R") #'ledger-report-reverse-report) - (define-key map (kbd "s") #'ledger-report-save) - (define-key map (kbd "S") #'ledger-report) - (define-key map (kbd "e") #'ledger-report-edit-report) - (define-key map (kbd "E") #'ledger-report-edit-reports) - (define-key map (kbd "q") #'ledger-report-quit) - (define-key map (kbd "C-c C-l C-r") #'ledger-report-redo) - (define-key map (kbd "C-c C-l C-S-s") #'ledger-report-save) - (define-key map (kbd "C-c C-l C-e") #'ledger-report-edit-report) - (define-key map (kbd "C-c C-o C-r") #'ledger-report) - (define-key map (kbd "M-p") #'ledger-report-previous-month) - (define-key map (kbd "M-n") #'ledger-report-next-month) - (define-key map (kbd "$") #'ledger-report-toggle-default-commodity) - map) - "Keymap for `ledger-report-mode'.") - -(easy-menu-define ledger-report-mode-menu ledger-report-mode-map - "Ledger report menu." - '("Reports" - ["Select Report" ledger-report] - ["Save Report" ledger-report-save] - ["Edit Current Report" ledger-report-edit-report] - ["Edit All Reports" ledger-report-edit-reports] - ["Re-run Report" ledger-report-redo] - "---" - ["Reverse report order" ledger-report-reverse-report] - "---" - ["Scroll Up" scroll-up] - ["Visit Source" ledger-report-visit-source] - ["Scroll Down" scroll-down] - "---" - ["Quit" ledger-report-quit] - )) - -(define-derived-mode ledger-report-mode special-mode "Ledger-Report" - "A mode for viewing ledger reports." - (setq-local revert-buffer-function #'ledger-report-redo) - (hack-dir-local-variables-non-file-buffer)) - -(defconst ledger-report--extra-args-marker "[[ledger-mode-flags]]") - -(defun ledger-report-binary-format-specifier () - "Return the path to ledger, plus a marker for extra arguments." - (list (shell-quote-argument ledger-binary-path) - ledger-report--extra-args-marker)) - -(defun ledger-report-tagname-format-specifier () - "Return a valid meta-data tag name." - ;; It is intended completion should be available on existing tag - ;; names, but it remains to be implemented. - (ledger-read-string-with-default "Tag Name" nil)) - -(defun ledger-report-tagvalue-format-specifier () - "Return a valid meta-data tag name." - ;; It is intended completion should be available on existing tag - ;; values, but it remains to be implemented. - (ledger-read-string-with-default "Tag Value" nil)) - -(defun ledger-report-read-name () - "Read the name of a ledger report to use, with completion. - -The empty string and unknown names are allowed." - (completing-read "Report name: " - ledger-reports nil nil nil - 'ledger-report-name-prompt-history nil)) - -(defun ledger-report (report-name edit) - "Run a user-specified report from `ledger-reports'. - -Prompts the user for the REPORT-NAME of the report to run or -EDIT. If no name is entered, the user will be prompted for a -command line to run. The command line specified or associated -with the selected report name is run and the output is made -available in another buffer for viewing. If a prefix argument is -given and the user selects a valid report name, the user is -prompted with the corresponding command line for editing before -the command is run. - -The output buffer will be in `ledger-report-mode', which defines -commands for saving a new named report based on the command line -used to generate the buffer, navigating the buffer, etc." - (interactive - (progn - (when (and (buffer-modified-p) - (y-or-n-p "Buffer modified, save it? ")) - (save-buffer)) - (let ((rname (ledger-report-read-name)) - (edit (not (null current-prefix-arg)))) - (list rname edit)))) - (let* ((file (ledger-master-file)) - (buf (find-file-noselect file))) - (with-current-buffer - (pop-to-buffer (get-buffer-create ledger-report-buffer-name)) - (with-silent-modifications - (erase-buffer) - (ledger-report-mode) - (setq ledger-report-saved nil) - (setq ledger-report-ledger-buf buf) - (setq ledger-report-name report-name) - (setq ledger-report-is-reversed nil) - (setq ledger-report-current-month nil) - (setq ledger-master-file file) - (ledger-do-report (ledger-report-cmd report-name edit))) - (ledger-report-maybe-shrink-window) - (run-hooks 'ledger-report-after-report-hook) - (message (substitute-command-keys (concat "\\[ledger-report-quit] to quit; " - "\\[ledger-report-redo] to redo; " - "\\[ledger-report-edit-report] to edit; " - "\\[ledger-report-save] to save; " - "\\[scroll-up-command] and \\[scroll-down-command] to scroll")))))) - -(defun ledger-report--header-function () - "Compute the string to be used as the header in the `ledger-report' buffer." - (format "Ledger Report: %s -- Buffer: %s -- Command: %s" - (propertize ledger-report-name 'face 'font-lock-constant-face) - (propertize (buffer-name ledger-report-ledger-buf) 'face 'font-lock-string-face) - (propertize ledger-report-cmd 'face 'font-lock-comment-face))) - -(defun ledger-report-name-exists (name) - "Check to see if the given report NAME exists. - -If exists, returns the object naming the report, otherwise -returns nil." - (unless (string-empty-p name) - (car (assoc name ledger-reports)))) - -(defun ledger-reports-add (name cmd) - "Add a new report NAME and CMD to `ledger-reports'." - (setq ledger-reports (cons (list name cmd) ledger-reports))) - -(defun ledger-reports-custom-save () - "Save the `ledger-reports' variable using the customize framework." - (customize-save-variable 'ledger-reports ledger-reports)) - -(defun ledger-report-read-command (report-cmd) - "Read the command line to create a report from REPORT-CMD." - (read-from-minibuffer "Report command line: " - (if (null report-cmd) "ledger " report-cmd) - nil nil 'ledger-report-cmd-prompt-history)) - -(defun ledger-report-ledger-file-format-specifier () - "Substitute the full path to master or current ledger file. - -The master file name is determined by the function -`ledger-master-file', which depends on the variable of the same -name. If it is non-nil, it is used, otherwise the current -buffer's file is used." - (ledger-master-file)) - -;; General helper functions - -(defun ledger-master-file () - "Return the master file for a ledger file. - -The master file is either the file for the current ledger buffer -or the file specified by the buffer-local variable -`ledger-master-file'. Typically this variable would be set in a -file local variable comment block at the end of a ledger file -which is included in some other file." - (if ledger-master-file - (expand-file-name ledger-master-file) - (buffer-file-name))) - -(defun ledger-report-payee-format-specifier () - "Substitute a payee name. - -The user is prompted to enter a payee and that is substituted. -If point is in an xact, the payee for that xact is used as the -default." - ;; It is intended completion should be available on existing - ;; payees, but the list of possible completions needs to be - ;; developed to allow this. - (if-let ((payee (ledger-xact-payee))) - (ledger-read-string-with-default "Payee" (regexp-quote payee)) - (ledger-read-string-with-default "Payee" nil))) - -(defun ledger-report-account-format-specifier () - "Substitute an account name. - -The user is prompted to enter an account name, which can be any -regular expression identifying an account. If point is on an -account posting line for an xact, the full account name on that -line is the default." - (ledger-read-account-with-prompt "Account")) - -(defun ledger-report--current-month () - "Return current month as (YEAR . MONTH-INDEX). - -MONTH-INDEX ranges from 1 (January) to 12 (December) and YEAR is -a number." - (let* ((time-parts (decode-time)) - (year (nth 5 time-parts)) - (month-index (nth 4 time-parts))) - (cons year month-index))) - -(defun ledger-report--normalize-month (month) - "Return (YEAR . NEW-MONTH) where NEW-MONTH is between 1 and 12. - -MONTH is of the form (YEAR . INDEX) where INDEX is an integer. -The purpose of this method is then to convert any year/month pair -to a meaningful date, e.g., from (2018 . -2) to (2017 . 10)." - (let* ((month-index (cdr month)) - (year-shift (/ (1- month-index) 12))) - (when (<= month-index 0) - (setq year-shift (1- year-shift))) - (cons (+ (car month) year-shift) - (1+ (mod (1- month-index) 12))))) - -(defun ledger-report--shift-month (month shift) - "Return (YEAR . NEW-MONTH) where NEW-MONTH is MONTH+SHIFT. - -MONTH is of the form (YEAR . INDEX) where INDEX ranges from -1 (January) to 12 (December) and YEAR is a number." - (let* ((year (car month)) - (new-month (+ (cdr month) shift))) - (ledger-report--normalize-month (cons year new-month)))) - -(defun ledger-report-month-format-specifier () - "Substitute current month." - (with-current-buffer (or ledger-report-buffer-name (current-buffer)) - (let* ((month (or ledger-report-current-month (ledger-report--current-month))) - (year (car month)) - (month-index (cdr month))) - (format "%s-%s" year month-index)))) - -(defun ledger-report-expand-format-specifiers (report-cmd) - "Expand format specifiers in REPORT-CMD with thing under point." - (save-match-data - (let ((expanded-cmd report-cmd)) - (set-match-data (list 0 0)) - (while (string-match "%(\\([^)]*\\))" expanded-cmd - (if (> (length expanded-cmd) (match-end 0)) - (match-end 0) - (1- (length expanded-cmd)))) - (let* ((specifier (match-string 1 expanded-cmd)) - (f (cdr (assoc specifier ledger-report-format-specifiers)))) - (if f - (let* ((arg (save-match-data - (with-current-buffer ledger-report-ledger-buf - (funcall f)))) - (quoted (if (listp arg) - (mapconcat #'identity arg " ") - (save-match-data - (shell-quote-argument arg))))) - (setq expanded-cmd (replace-match quoted t t expanded-cmd)))))) - expanded-cmd))) - -(defun ledger-report--cmd-needs-links-p (cmd) - "Check links should be added to the report produced by CMD." - ;; --subtotal reports do not produce identifiable transactions, so - ;; don't prepend location information for them - (and (string-match "\\<reg\\(ister\\)?\\>" cmd) - ledger-report-links-in-register - (not (string-match "--subtotal" cmd)))) - -(defun ledger-report--compute-extra-args (report-cmd) - "Compute extra args to add to REPORT-CMD." - `(,@(when (ledger-report--cmd-needs-links-p report-cmd) - '("--prepend-format=%(filename):%(beg_line):")) - ,@(when ledger-report-auto-width - `("--columns" ,(format "%d" (window-max-chars-per-line)))) - ,@(when ledger-report-use-native-highlighting - ledger-report-native-highlighting-arguments) - ,@(when ledger-report-use-strict - '("--strict")))) - -(defun ledger-report-cmd (report-name edit) - "Get the command line to run the report name REPORT-NAME. -Optionally EDIT the command." - (let ((report-cmd (car (cdr (assoc report-name ledger-reports))))) - ;; logic for substitution goes here - (when (or (null report-cmd) edit) - (setq report-cmd (ledger-report-read-command report-cmd)) - (setq ledger-report-saved nil)) ;; this is a new report, or edited report - (setq report-cmd (ledger-report-expand-format-specifiers report-cmd)) - (setq ledger-report-cmd report-cmd) - (or (string-empty-p report-name) - (ledger-report-name-exists report-name) - (progn - (ledger-reports-add report-name report-cmd) - (ledger-reports-custom-save))) - report-cmd)) - -(define-button-type 'ledger-report-register-entry - 'follow-link t - 'face nil ;; Otherwise make-text-button replaces Ledger's native highlighting - 'action (lambda (_button) (ledger-report-visit-source))) - -(defun ledger-report--change-month (shift) - "Rebuild report with transactions from current month + SHIFT." - (let* ((current-month (or ledger-report-current-month (ledger-report--current-month))) - (previous-month (ledger-report--shift-month current-month shift))) - (setq ledger-report-current-month previous-month) - (ledger-report-cmd ledger-report-name nil) - (ledger-report-redo))) - -(defun ledger-report--add-links () - "Replace file and line annotations with buttons." - (while (re-search-forward "^\\(\\(?:/\\|[a-zA-Z]:[\\/]\\)[^:]+\\)?:\\([0-9]+\\)?:" nil t) - (let ((file (match-string 1)) - (line (string-to-number (match-string 2)))) - (delete-region (match-beginning 0) (match-end 0)) - (when (and file line) - (add-text-properties (line-beginning-position) (line-end-position) - (list 'ledger-source (cons file line))) - (make-text-button - (line-beginning-position) (line-end-position) - 'type 'ledger-report-register-entry - 'help-echo (format "mouse-2, RET: Visit %s:%d" file line)) - ;; Appending the face preserves Ledger's native highlighting - (font-lock-append-text-property (line-beginning-position) (line-end-position) - 'face 'ledger-font-report-clickable-face) - (end-of-line))))) - -(defun ledger-report--compute-header-line (cmd) - "Call `ledger-report-header-line-fn' with `ledger-report-cmd' bound to CMD." - (let ((ledger-report-cmd cmd)) - (funcall ledger-report-header-line-fn))) - -(defun ledger-do-report (cmd) - "Run a report command line CMD. -CMD may contain a (shell-quoted) version of -`ledger-report--extra-args-marker', which will be replaced by -arguments returned by `ledger-report--compute-extra-args'." - (goto-char (point-min)) - (let* ((marker ledger-report--extra-args-marker) - (marker-re (concat " *" (regexp-quote marker))) - (args (ledger-report--compute-extra-args cmd)) - (args-str (concat " " (mapconcat #'shell-quote-argument args " "))) - (clean-cmd (replace-regexp-in-string marker-re "" cmd t t)) - (real-cmd (replace-regexp-in-string marker-re args-str cmd t t))) - (setq header-line-format - (and ledger-report-use-header-line - `(:eval (ledger-report--compute-header-line ,clean-cmd)))) - (unless ledger-report-use-header-line - (insert (format "Report: %s\n" ledger-report-name) - (format "Command: %s\n" clean-cmd) - (make-string (- (window-width) 1) ?=) - "\n\n")) - (let* ((report (shell-command-to-string real-cmd))) - (when ledger-report-use-native-highlighting - (setq report (ansi-color-apply report))) - (save-excursion - (insert report)) - (when (ledger-report--cmd-needs-links-p cmd) - (save-excursion - (ledger-report--add-links)))))) - -(defun ledger-report-visit-source () - "Visit the transaction under point in the report window." - (interactive) - (let* ((prop (get-text-property (point) 'ledger-source)) - (file (car prop)) - (line (cdr prop))) - (when (and file line) - (find-file-other-window file) - (widen) - (goto-char (point-min)) - (forward-line (1- line)) - (ledger-navigate-beginning-of-xact)))) - -(defun ledger-report-goto () - "Goto the ledger report buffer." - (interactive) - (let ((rbuf (get-buffer ledger-report-buffer-name))) - (if (not rbuf) - (error "There is no ledger report buffer")) - (pop-to-buffer rbuf) - (ledger-report-maybe-shrink-window))) - -(defun ledger-report-redo (&optional _ignore-auto _noconfirm) - "Redo the report in the current ledger report buffer. -IGNORE-AUTO and NOCONFIRM are for compatibility with -`revert-buffer-function' and are currently ignored." - (interactive) - (unless (or (derived-mode-p 'ledger-mode) - (derived-mode-p 'ledger-report-mode)) - (user-error "Not in a ledger-mode or ledger-report-mode buffer")) - (let ((cur-buf (current-buffer))) - (when (and ledger-report-auto-refresh - (get-buffer ledger-report-buffer-name)) - (pop-to-buffer (get-buffer ledger-report-buffer-name)) - (ledger-report-maybe-shrink-window) - (setq ledger-report-cursor-line-number (line-number-at-pos)) - (with-silent-modifications - (erase-buffer) - (ledger-do-report ledger-report-cmd) - (when ledger-report-is-reversed - (ledger-report-reverse-lines)) - (when ledger-report-auto-refresh-sticky-cursor - (forward-line (- ledger-report-cursor-line-number 5)))) - (run-hooks 'ledger-report-after-report-hook) - (pop-to-buffer cur-buf)))) - -(defun ledger-report-quit () - "Quit the ledger report buffer and kill its buffer." - (interactive) - (unless (buffer-live-p (get-buffer ledger-report-buffer-name)) - (user-error "No ledger report buffer")) - (quit-windows-on ledger-report-buffer-name 'kill)) - -(define-obsolete-function-alias 'ledger-report-kill #'ledger-report-quit "2018-03-18") - -(defun ledger-report-edit-reports () - "Edit the defined ledger reports." - (interactive) - (customize-variable 'ledger-reports)) - -(defun ledger-report-edit-report () - "Edit the current report command in the mini buffer and re-run the report." - (interactive) - (setq ledger-report-cmd (ledger-report-read-command ledger-report-cmd)) - (ledger-report-redo)) - -(define-obsolete-function-alias 'ledger-report-select-report #'ledger-report "ledger 4.0.0") - -(defun ledger-report-read-new-name () - "Read the name for a new report from the minibuffer." - (let ((name "")) - (while (string-empty-p name) - (setq name (read-from-minibuffer "Report name: " nil nil nil - 'ledger-report-name-prompt-history))) - name)) - -(defun ledger-report-save () - "Save the current report command line as a named report." - (interactive) - (ledger-report-goto) - (when (string-empty-p ledger-report-name) - (setq ledger-report-name (ledger-report-read-new-name))) - - (when-let ((existing-name (ledger-report-name-exists ledger-report-name))) - (cond ((y-or-n-p (format "Overwrite existing report named '%s'? " - ledger-report-name)) - (if (string-equal - ledger-report-cmd - (car (cdr (assq existing-name ledger-reports)))) - (message "Nothing to save. Current command is identical to existing saved one") - (setq ledger-reports - (assq-delete-all existing-name ledger-reports)) - (ledger-reports-add ledger-report-name ledger-report-cmd) - (ledger-reports-custom-save))) - (t - (setq ledger-report-name (ledger-report-read-new-name)) - (ledger-reports-add ledger-report-name ledger-report-cmd) - (ledger-reports-custom-save))))) - -(defun ledger-report-previous-month () - "Rebuild report with transactions from the previous month." - (interactive) - (ledger-report--change-month -1)) - -(defun ledger-report-next-month () - "Rebuild report with transactions from the next month." - (interactive) - (ledger-report--change-month 1)) - -(defun ledger-report-toggle-default-commodity () - "Toggle exchange of reported amounts to `ledger-reconcile-default-commodity'." - (interactive) - (unless (derived-mode-p 'ledger-report-mode) - (user-error "Not a ledger report buffer")) - (save-match-data - (if (string-match - (concat (rx (or "--exchange" "-X") (1+ space)) - (regexp-quote ledger-reconcile-default-commodity)) - ledger-report-cmd) - (setq ledger-report-cmd (replace-match "" nil nil ledger-report-cmd)) - (setq ledger-report-cmd (concat ledger-report-cmd - " --exchange " ledger-reconcile-default-commodity)))) - (ledger-report-redo)) - -(provide 'ledger-report) - -;;; ledger-report.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-report.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-report.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-schedule.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-schedule.el @@ -1,332 +0,0 @@ -;;; ledger-schedule.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2013 Craig Earls (enderw88 at gmail dot com) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) -;; any later version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - -;;; Commentary: -;; -;; This module provides for automatically adding transactions to a -;; ledger buffer on a periodic basis. Recurrence expressions are -;; inspired by Martin Fowler's "Recurring Events for Calendars", -;; martinfowler.com/apsupp/recurring.pdf - -;; use (fset 'VARNAME (macro args)) to put the macro definition in the -;; function slot of the symbol VARNAME. Then use VARNAME as the -;; function without have to use funcall. - - -(require 'ledger-init) -(require 'cl-lib) - -(declare-function ledger-mode "ledger-mode") -;;; Code: - -(defgroup ledger-schedule nil - "Support for automatically recommendation transactions." - :group 'ledger) - -(defcustom ledger-schedule-buffer-name "*Ledger Schedule*" - "Name for the schedule buffer." - :type 'string - :group 'ledger-schedule) - -(defcustom ledger-schedule-look-backward 7 - "Number of days to look back in time for transactions." - :type 'integer - :group 'ledger-schedule) - -(defcustom ledger-schedule-look-forward 14 - "Number of days auto look forward to recommend transactions." - :type 'integer - :group 'ledger-schedule) - -(defcustom ledger-schedule-file "~/ledger-schedule.ledger" - "File to find scheduled transactions." - :type 'file - :group 'ledger-schedule) - -(defcustom ledger-schedule-week-days '(("Mo" 1) - ("Tu" 2) - ("We" 3) - ("Th" 4) - ("Fr" 5) - ("Sa" 6) - ("Su" 0)) - "List of weekday abbreviations. -There must be exactly seven entries each with a two character -abbreviation for a day and the number of that day in the week." - :type '(alist :value-type (group integer)) - :group 'ledger-schedule) - -(defsubst ledger-between (val low high) - "Return TRUE if VAL >= LOW and <= HIGH." - (declare (obsolete <= "Ledger-mode v4.0.1")) - (<= low val high)) - -(defun ledger-schedule-days-in-month (month year) - "Return number of days in the MONTH, MONTH is from 1 to 12. -If YEAR is nil, assume it is not a leap year" - (if (<= 1 month 12) - (if (and year (date-leap-year-p year) (= 2 month)) - 29 - (nth (1- month) '(31 28 31 30 31 30 31 31 30 31 30 31))) - (error "Month out of range, MONTH=%S" month))) - -(defun ledger-schedule-encode-day-of-week (day-string) - "Return the numerical day of week corresponding to DAY-STRING." - (cadr (assoc day-string ledger-schedule-week-days))) - -;; Macros to handle date expressions - -(defun ledger-schedule-constrain-day-in-month (count day-of-week) - "Return a form that returns TRUE for the the COUNT DAY-OF-WEEK. -For example, return true if date is the 3rd Thursday of the -month. Negative COUNT starts from the end of the month. (EQ -COUNT 0) means EVERY day-of-week (eg. every Saturday)" - (if (and (<= -6 count 6) (<= 0 day-of-week 6)) - (cond ((zerop count) ;; Return true if day-of-week matches - `(eq (nth 6 (decode-time date)) ,day-of-week)) - ((> count 0) ;; Positive count - (let ((decoded (cl-gensym))) - `(let ((,decoded (decode-time date))) - (and (eq (nth 6 ,decoded) ,day-of-week) - (<= ,(* (1- count) 7) - (nth 3 ,decoded) - ,(* count 7)))))) - ((< count 0) - (let ((days-in-month (cl-gensym)) - (decoded (cl-gensym))) - `(let* ((,decoded (decode-time date)) - (,days-in-month (ledger-schedule-days-in-month - (nth 4 ,decoded) - (nth 5 ,decoded)))) - (and (eq (nth 6 ,decoded) ,day-of-week) - (<= (+ ,days-in-month ,(* count 7)) - (nth 3 ,decoded) - (+ ,days-in-month ,(* (1+ count) 7))))))) - (t - (error "COUNT out of range, COUNT=%S" count))) - (error "Invalid argument to ledger-schedule-day-in-month-macro %S %S" - count - day-of-week))) - -(defun ledger-schedule-constrain-every-count-day (day-of-week skip start-date) - "Return a form that is true for every DAY-OF-WEEK. -Skips SKIP, and starts on START-DATE. -For example every second Friday, regardless of month." - (let ((start-day (nth 6 (decode-time start-date)))) - (if (eq start-day day-of-week) ;; good, can proceed - `(zerop (mod (- (time-to-days date) ,(time-to-days start-date)) ,(* skip 7))) - (error "START-DATE day of week doesn't match DAY-OF-WEEK")))) - -(defun ledger-schedule-constrain-date-range (month1 day1 month2 day2) - "Return a form of DATE that is true if DATE falls between two dates. -The dates are given by the pairs MONTH1 DAY1 and MONTH2 DAY2." - (let ((decoded (cl-gensym)) - (target-month (cl-gensym)) - (target-day (cl-gensym))) - `(let* ((,decoded (decode-time date)) - (,target-month (nth 4 decoded)) - (,target-day (nth 3 decoded))) - (and (and (> ,target-month ,month1) - (< ,target-month ,month2)) - (and (> ,target-day ,day1) - (< ,target-day ,day2)))))) - - - -(defun ledger-schedule-scan-transactions (schedule-file) - "Scan SCHEDULE-FILE and return a list of transactions with date predicates. -The car of each item is a function of date that returns true if -the transaction should be logged for that day." - (interactive "fFile name: ") - (let ((xact-list (list))) - (with-current-buffer - (find-file-noselect schedule-file) - (goto-char (point-min)) - (while (re-search-forward "^\\[\\(.*\\)\\] " nil t) - (let ((date-descriptor "") - (transaction nil) - (xact-start (match-end 0))) - (setq date-descriptor - (ledger-schedule-read-descriptor-tree - (buffer-substring-no-properties - (match-beginning 0) - (match-end 0)))) - (forward-paragraph) - (setq transaction (list date-descriptor - (buffer-substring-no-properties - xact-start - (point)))) - (setq xact-list (cons transaction xact-list)))) - xact-list))) - -(defun ledger-schedule-read-descriptor-tree (descriptor-string) - "Read DESCRIPTOR-STRING and return a form that evaluates dates." - (ledger-schedule-transform-auto-tree - (split-string - (substring descriptor-string 1 (string-match "]" descriptor-string)) " "))) - -(defun ledger-schedule-transform-auto-tree (descriptor-string-list) - "Take DESCRIPTOR-STRING-LIST, and return a string with a lambda function of date." - ;; use funcall to use the lambda function spit out here - (if (consp descriptor-string-list) - (let (result) - (while (consp descriptor-string-list) - (let ((newcar (car descriptor-string-list))) - (if (consp newcar) - (setq newcar (ledger-schedule-transform-auto-tree (car descriptor-string-list)))) - ;; newcar may be a cons now, after ledger-schedule-transfrom-auto-tree - (if (consp newcar) - (push newcar result) - ;; this is where we actually turn the string descriptor into useful lisp - (push (ledger-schedule-compile-constraints newcar) result)) ) - (setq descriptor-string-list (cdr descriptor-string-list))) - - ;; tie up all the clauses in a big or lambda, and return - ;; the lambda function as list to be executed by funcall - `(lambda (date) - ,(nconc (list 'or) (nreverse result) descriptor-string-list))))) - -(defun ledger-schedule-compile-constraints (descriptor-string) - "Return a list with the year, month and day fields split." - (let ((fields (split-string descriptor-string "[/\\-]" t))) - (list 'and - (ledger-schedule-constrain-day (nth 0 fields) (nth 1 fields) (nth 2 fields)) - (ledger-schedule-constrain-year (nth 0 fields) (nth 1 fields) (nth 2 fields)) - (ledger-schedule-constrain-month (nth 0 fields) (nth 1 fields) (nth 2 fields))))) - -(defun ledger-schedule-constrain-year (year-desc month-desc day-desc) - "Return a form that constrains the year. - -YEAR-DESC, MONTH-DESC, and DAY-DESC are the string portions of the -date descriptor." - (cond - ((string-match "[A-Za-z]" day-desc) t) ; there is an advanced day descriptor which overrides the year - ((string= year-desc "*") t) - ((/= 0 (string-to-number year-desc)) - `(memq (nth 5 (decode-time date)) ',(mapcar 'string-to-number (split-string year-desc ",")))) - (t - (error "Improperly specified year constraint: %s %s %s" year-desc month-desc day-desc)))) - -(defun ledger-schedule-constrain-month (year-desc month-desc day-desc) - "Return a form that constrains the month. - -YEAR-DESC, MONTH-DESC, and DAY-DESC are the string portions of the -date descriptor." - (cond - ((string-match "[A-Za-z]" day-desc) t) ; there is an advanced day descriptor which overrides the month - ((string= month-desc "*") - t) ;; always match - ((string= month-desc "E") ;; Even - `(cl-evenp (nth 4 (decode-time date)))) - ((string= month-desc "O") ;; Odd - `(cl-oddp (nth 4 (decode-time date)))) - ((/= 0 (string-to-number month-desc)) ;; Starts with number - `(memq (nth 4 (decode-time date)) ',(mapcar 'string-to-number (split-string month-desc ",")))) - (t - (error "Improperly specified month constraint: %s %s %s" year-desc month-desc day-desc)))) - -(defun ledger-schedule-constrain-day (year-desc month-desc day-desc) - "Return a form that constrains the day. - -YEAR-DESC, MONTH-DESC, and DAY-DESC are the string portions of the -date descriptor." - (cond ((string= day-desc "*") - t) - ((string= day-desc "L") - `(= (nth 3 (decode-time date)) (ledger-schedule-days-in-month (nth 4 (decode-time date)) (nth 5 (decode-time date))))) - ((string-match "[A-Za-z]" day-desc) ;; There is something other than digits and commas - (ledger-schedule-parse-complex-date year-desc month-desc day-desc)) - ((/= 0 (string-to-number day-desc)) - `(memq (nth 3 (decode-time date)) ',(mapcar 'string-to-number (split-string day-desc ",")))) - (t - (error "Improperly specified day constraint: %s %s %s" year-desc month-desc day-desc)))) - - - -(defun ledger-schedule-parse-complex-date (year-desc month-desc day-desc) - "Parse day descriptors that have repeats." - (let ((years (mapcar 'string-to-number (split-string year-desc ","))) - (months (mapcar 'string-to-number (split-string month-desc ","))) - (day-parts (split-string day-desc "\\+")) - (every-nth (string-match "\\+" day-desc))) - (if every-nth - (let ((base-day (string-to-number (car day-parts))) - (increment (string-to-number (substring (cadr day-parts) 0 - (string-match "[A-Za-z]" (cadr day-parts))))) - (day-of-week (ledger-schedule-encode-day-of-week - (substring (cadr day-parts) (string-match "[A-Za-z]" (cadr day-parts)))))) - (ledger-schedule-constrain-every-count-day day-of-week increment (encode-time 0 0 0 base-day (car months) (car years)))) - (let ((count (string-to-number (substring (car day-parts) 0 1))) - (day-of-week (ledger-schedule-encode-day-of-week - (substring (car day-parts) (string-match "[A-Za-z]" (car day-parts)))))) - (ledger-schedule-constrain-day-in-month count day-of-week))))) - -(defun ledger-schedule-list-upcoming-xacts (candidate-items early horizon) - "Search CANDIDATE-ITEMS for xacts that occur within the given period. -The period runs from (today - EARLY) to (today + HORIZON)." - (let ((start-date (time-subtract (current-time) (days-to-time early))) - test-date items) - (cl-loop for day from 0 to (+ early horizon) by 1 do - (setq test-date (time-add start-date (days-to-time day))) - (dolist (candidate candidate-items items) - (if (funcall (car candidate) test-date) - (setq items (append items (list (list test-date (cadr candidate)))))))) - items)) - -(defun ledger-schedule-create-auto-buffer (candidate-items early horizon) - "Format CANDIDATE-ITEMS for display." - (let ((candidates (ledger-schedule-list-upcoming-xacts candidate-items early horizon)) - (schedule-buf (get-buffer-create ledger-schedule-buffer-name))) - (with-current-buffer schedule-buf - (erase-buffer) - (dolist (candidate candidates) - (insert (ledger-format-date (car candidate) ) " " (cadr candidate) "\n")) - (ledger-mode)) - (length candidates))) - -(defun ledger-schedule-upcoming (file look-backward look-forward) - "Generate upcoming transactions. - -FILE is the file containing the scheduled transaction, -default to `ledger-schedule-file'. -LOOK-BACKWARD is the number of days in the past to look at -default to `ledger-schedule-look-backward' -LOOK-FORWARD is the number of days in the future to look at -default to `ledger-schedule-look-forward' - -Use a prefix arg to change the default value" - (interactive (if current-prefix-arg - (list (read-file-name "Schedule File: " () ledger-schedule-file t) - (read-number "Look backward: " ledger-schedule-look-backward) - (read-number "Look forward: " ledger-schedule-look-forward)) - (list ledger-schedule-file ledger-schedule-look-backward ledger-schedule-look-forward))) - (unless (and file (file-exists-p file)) - (error "Could not find ledger schedule file at %s" file)) - (ledger-schedule-create-auto-buffer - (ledger-schedule-scan-transactions file) - look-backward - look-forward) - (pop-to-buffer ledger-schedule-buffer-name)) - - -(provide 'ledger-schedule) - -;;; ledger-schedule.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-schedule.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-schedule.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-sort.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-sort.el @@ -1,119 +0,0 @@ -;;; ledger-sort.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - - - -;;; Commentary: -;; - -;;; Code: -(require 'ledger-regex) -(require 'ledger-navigate) -(require 'ledger-xact) - -(defun ledger-sort-find-start () - "Find the beginning of a sort region." - (when (re-search-forward ";.*Ledger-mode:.*Start sort" nil t) - (match-end 0))) - -(defun ledger-sort-find-end () - "Find the end of a sort region." - (when (re-search-forward ";.*Ledger-mode:.*End sort" nil t) - (match-end 0))) - -(defun ledger-sort-insert-start-mark () - "Insert a marker to start a sort region." - (interactive) - (save-excursion - (goto-char (point-min)) - (when (ledger-sort-find-start) - (delete-region (match-beginning 0) (match-end 0)))) - (beginning-of-line) - (insert "\n; Ledger-mode: Start sort\n\n")) - -(defun ledger-sort-insert-end-mark () - "Insert a marker to end a sort region." - (interactive) - (save-excursion - (goto-char (point-min)) - (when (ledger-sort-find-end) - (delete-region (match-beginning 0) (match-end 0)))) - (beginning-of-line) - (insert "\n; Ledger-mode: End sort\n\n")) - -(defun ledger-sort-startkey () - "Return a numeric sort key based on the date of the xact beginning at point." - ;; Can use `time-convert' to return an integer instead of a floating-point - ;; number, starting in Emacs 27. - (float-time - (ledger-parse-iso-date - (buffer-substring-no-properties (point) (+ 10 (point)))))) - -(defun ledger-sort-region (beg end) - "Sort the region from BEG to END in chronological order." - (interactive "r") ;; load beg and end from point and mark - ;; automagically - (let* ((bounds (ledger-navigate-find-xact-extents (point))) - (point-delta (- (point) (car bounds))) - (target-xact (buffer-substring (car bounds) (cadr bounds))) - (inhibit-modification-hooks t)) - (save-excursion - (save-restriction - (goto-char beg) - ;; make sure beg of region is at the beginning of a line - (beginning-of-line) - ;; make sure point is at the beginning of a xact - (unless (looking-at ledger-payee-any-status-regex) - (ledger-navigate-next-xact)) - (setq beg (point)) - (goto-char end) - (ledger-navigate-next-xact) - ;; make sure end of region is at the beginning of next record - ;; after the region - (setq end (point)) - (narrow-to-region beg end) - (goto-char beg) - - (let ((inhibit-field-text-motion t)) - (sort-subr - nil - #'ledger-navigate-next-xact - #'ledger-navigate-end-of-xact - #'ledger-sort-startkey)))) - - (goto-char (point-min)) - (search-forward target-xact) - (goto-char (+ (match-beginning 0) point-delta)))) - -(defun ledger-sort-buffer () - "Sort the entire buffer." - (interactive) - (let (sort-start sort-end) - (save-excursion - (goto-char (point-min)) - (setq sort-start (ledger-sort-find-start) - sort-end (ledger-sort-find-end))) - (ledger-sort-region (or sort-start (point-min)) - (or sort-end (point-max))))) - -(provide 'ledger-sort) - -;;; ledger-sort.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-sort.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-sort.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-state.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-state.el @@ -1,259 +0,0 @@ -;;; ledger-state.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - - -;;; Commentary: -;; Utilities for dealing with transaction and posting status. - -;;; Code: -(require 'ledger-navigate) -(require 'ledger-context) - -(defcustom ledger-clear-whole-transactions nil - "If non-nil, clear whole transactions, not individual postings." - :type 'boolean - :group 'ledger) - -(defun ledger-transaction-state () - "Return the state of the transaction at point." - (save-excursion - (when (or (looking-at "^[0-9]") - (re-search-backward "^[0-9]" nil t)) - (skip-chars-forward "0-9./=\\-") - (skip-syntax-forward " ") - (cond ((looking-at "!\\s-*") 'pending) - ((looking-at "\\*\\s-*") 'cleared) - (t nil))))) - -(defun ledger-posting-state () - "Return the state of the posting." - (save-excursion - (goto-char (line-beginning-position)) - (skip-syntax-forward " ") - (cond ((looking-at "!\\s-*") 'pending) - ((looking-at "\\*\\s-*") 'cleared) - (t (ledger-transaction-state))))) - -(defun ledger-char-from-state (state) - "Return the char representation of STATE." - (if state - (if (eq state 'pending) - "!" - "*") - "")) - -(defun ledger-state-from-char (state-char) - "Get state from STATE-CHAR." - (cond ((eql state-char ?\!) 'pending) - ((eql state-char ?\*) 'cleared) - ((eql state-char ?\;) 'comment) - (t nil))) - - -(defun ledger-state-from-string (state-string) - "Get state from STATE-STRING." - (when state-string - (cond - ((string-match "!" state-string) 'pending) - ((string-match "\\*" state-string) 'cleared) - ((string-match ";" state-string) 'comment) - (t nil)))) - -(defun ledger-toggle-current-posting (&optional style) - "Toggle the cleared status of the transaction under point. -Optional argument STYLE may be `pending' or `cleared', depending -on which type of status the caller wishes to indicate (default is -`cleared'). Returns the new status as `pending' `cleared' or nil. -This function is rather complicated because it must preserve both -the overall formatting of the ledger xact, as well as ensuring -that the most minimal display format is used. This could be -achieved more certainly by passing the xact to ledger for -formatting, but doing so causes inline math expressions to be -dropped." - (interactive) - (let ((bounds (ledger-navigate-find-xact-extents (point))) - new-status cur-status) - ;; Uncompact the xact, to make it easier to toggle the - ;; transaction - (save-excursion ;; this excursion checks state of entire - ;; transaction and unclears if marked - (goto-char (car bounds)) ;; beginning of xact - (skip-chars-forward "0-9./=\\-") ;; skip the date - (skip-chars-forward " \t") ;; skip the white space after the date - (setq cur-status (and (member (char-after) '(?\* ?\!)) - (ledger-state-from-char (char-after)))) - ;;if cur-status if !, or * then delete the marker - (when cur-status - (let ((here (point))) - (skip-chars-forward "*! ") - (let ((width (- (point) here))) - (when (> width 0) - (delete-region here (point)) - (if (search-forward " " (line-end-position) t) - (insert (make-string width ? )))))) - (forward-line) - ;; Shift the cleared/pending status to the postings - (while (looking-at "[ \t]") - (skip-chars-forward " \t") - (when (not (eq (ledger-state-from-char (char-after)) 'comment)) - (insert (ledger-char-from-state cur-status) " ") - (if (and (search-forward " " (line-end-position) t) - (looking-at " ")) - (delete-char 2))) - (forward-line)) - (setq new-status nil))) - - ;;this excursion toggles the posting status - (save-excursion - (setq inhibit-modification-hooks t) - - (goto-char (line-beginning-position)) - (when (looking-at "[ \t]") - (skip-chars-forward " \t") - (let ((here (point)) - (cur-status (ledger-state-from-char (char-after)))) - (skip-chars-forward "*! ") - (let ((width (- (point) here))) - (when (> width 0) - (delete-region here (point)) - (save-excursion - (if (search-forward " " (line-end-position) t) - (insert (make-string width ? )))))) - (let (inserted) - (if cur-status - (if (and style (eq style 'cleared)) - (progn - (insert "* ") - (setq inserted 'cleared))) - (if (and style (eq style 'pending)) - (progn - (insert "! ") - (setq inserted 'pending)) - (progn - (insert "* ") - (setq inserted 'cleared)))) - (if (and inserted - (re-search-forward "\\(\t\\| [ \t]\\)" - (line-end-position) t)) - (cond - ((looking-at "\t") - (delete-char 1)) - ((looking-at " [ \t]") - (delete-char 2)) - ((looking-at " ") - (delete-char 1)))) - (setq new-status inserted)))) - (setq inhibit-modification-hooks nil)) - - ;; This excursion cleans up the xact so that it displays - ;; minimally. This means that if all posts are cleared, remove - ;; the marks and clear the entire transaction. - (save-excursion - (goto-char (car bounds)) - (forward-line) - (let ((first t) - (state nil) - (hetero nil)) - (while (and (not hetero) (looking-at "[ \t]")) - (skip-chars-forward " \t") - (let ((cur-status (ledger-state-from-char (char-after)))) - (if (not (eq cur-status 'comment)) - (if first - (setq state cur-status - first nil) - (if (not (eq state cur-status)) - (setq hetero t))))) - (forward-line)) - (when (and (not hetero) (not (eq state nil))) - (goto-char (car bounds)) - (forward-line) - (while (looking-at "[ \t]") - (skip-chars-forward " \t") - (let ((here (point))) - (skip-chars-forward "*! ") - (let ((width (- (point) here))) - (when (> width 0) - (delete-region here (point)) - (if (re-search-forward "\\(\t\\| [ \t]\\)" - (line-end-position) t) - (insert (make-string width ? )))))) - (forward-line)) - (goto-char (car bounds)) - (skip-chars-forward "0-9./=\\-") ;; Skip the date - (skip-chars-forward " \t") ;; Skip the white space - (insert (ledger-char-from-state state) " ") - (setq new-status state) - (if (re-search-forward "\\(\t\\| [ \t]\\)" - (line-end-position) t) - (cond - ((looking-at "\t") - (delete-char 1)) - ((looking-at " [ \t]") - (delete-char 2)) - ((looking-at " ") - (delete-char 1))))))) - new-status)) - -(defun ledger-toggle-current (&optional style) - "Toggle the current thing at point with optional STYLE." - (interactive) - (let ((thing (ledger-thing-at-point))) - (if (or (and ledger-clear-whole-transactions (eq 'posting thing)) - (eq 'transaction thing)) - (let ((end (save-excursion (ledger-navigate-end-of-xact) (point-marker)))) - ;; clear state markings on postings - (save-excursion - (forward-line) - (beginning-of-line) - (while (< (point) end) - (when (looking-at "\\s-+[*!]") - (ledger-toggle-current-posting style)) - (forward-line))) - (set-marker end nil) - (ledger-toggle-current-transaction style)) - (ledger-toggle-current-posting style)))) - -(defun ledger-toggle-current-transaction (&optional style) - "Toggle the transaction at point using optional STYLE." - (interactive) - (save-excursion - (when (or (looking-at "^[0-9]") - (re-search-backward "^[0-9]" nil t)) - (skip-chars-forward "0-9./=\\-") - (delete-horizontal-space) - (if (or (eq (ledger-state-from-char (char-after)) 'pending) - (eq (ledger-state-from-char (char-after)) 'cleared)) - (progn - (delete-char 1) - (when (and style (eq style 'cleared)) - (insert " *") - 'cleared)) - (if (and style (eq style 'pending)) - (progn - (insert " ! ") - 'pending) - (progn - (insert " * ") - 'cleared)))))) - -(provide 'ledger-state) - -;;; ledger-state.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-state.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-state.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-test.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-test.el @@ -1,137 +0,0 @@ -;;; ledger-test.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - -;;; Commentary: - -;;; Code: - -(declare-function ledger-mode "ledger-mode") ; TODO: fix this cyclic dependency -(require 'org) -(require 'outline) - -(defgroup ledger-test nil - "Definitions for the Ledger testing framework" - :group 'ledger) - -(defcustom ledger-source-directory "~/ledger/" - "Directory where the Ledger sources are located." - :type 'directory - :group 'ledger-test) - -(defcustom ledger-test-binary "/Products/ledger/debug/ledger" - "Directory where the Ledger debug binary is located." - :type 'file - :group 'ledger-test) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun ledger-create-test () - "Create a regression test." - (interactive) - (save-restriction - (org-narrow-to-subtree) - (save-excursion - (let (text beg) - (goto-char (point-min)) - (forward-line 1) - (setq beg (point)) - (search-forward ":PROPERTIES:") - (goto-char (line-beginning-position)) - (setq text (buffer-substring-no-properties beg (point))) - (goto-char (point-min)) - (re-search-forward ":ID:\\s-+\\([^-]+\\)") - (find-file-other-window - (format "~/src/ledger/test/regress/%s.test" (match-string 1))) - (sit-for 0) - (insert text) - (goto-char (point-min)) - (while (not (eobp)) - (goto-char (line-beginning-position)) - (delete-char 3) - (forward-line 1)))))) - -(defun ledger-test-org-narrow-to-entry () - (outline-back-to-heading) - (narrow-to-region (point) (progn (outline-next-heading) (point))) - (goto-char (point-min))) - -(defun ledger-test-create () - (interactive) - (let ((uuid (org-entry-get (point) "ID"))) - (when (string-match "\\`\\([^-]+\\)-" uuid) - (let ((prefix (match-string 1 uuid)) - input output) - (save-restriction - (ledger-test-org-narrow-to-entry) - (goto-char (point-min)) - (while (re-search-forward "#\\+begin_src ledger" nil t) - (goto-char (match-end 0)) - (forward-line 1) - (let ((beg (point))) - (re-search-forward "#\\+end_src") - (setq input - (concat (or input "") - (buffer-substring beg (match-beginning 0)))))) - (goto-char (point-min)) - (while (re-search-forward ":OUTPUT:" nil t) - (goto-char (match-end 0)) - (forward-line 1) - (let ((beg (point))) - (re-search-forward ":END:") - (setq output - (concat (or output "") - (buffer-substring beg (match-beginning 0))))))) - (find-file-other-window - (expand-file-name (concat prefix ".test") - (expand-file-name "test/regress" - ledger-source-directory))) - (ledger-mode) - (if input - (insert input) - (insert "2012-03-17 Payee\n") - (insert " Expenses:Food $20\n") - (insert " Assets:Cash\n")) - (insert "\ntest reg\n") - (if output - (insert output)) - (insert "end test\n"))))) - -(defun ledger-test-run () - (interactive) - (save-excursion - (goto-char (point-min)) - (when (re-search-forward "^test \\(.+?\\)\\( ->.*\\)?$" nil t) - (let ((command (expand-file-name ledger-test-binary)) - (args (format "--args-only --columns=80 --no-color -f \"%s\" %s" - buffer-file-name (match-string 1)))) - (setq args (replace-regexp-in-string "\\$sourcepath" - ledger-source-directory args)) - (kill-new args) - (message "Testing: ledger %s" args) - (let ((prev-directory default-directory)) - (cd ledger-source-directory) - (unwind-protect - (async-shell-command (format "\"%s\" %s" command args)) - (cd prev-directory))))))) - -(provide 'ledger-test) - -;;; ledger-test.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-test.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-test.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-texi.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-texi.el @@ -1,178 +0,0 @@ -;;; ledger-texi.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - -;;; Commentary: -;; - -;;; Code: -(defvar ledger-binary-path) - -(defgroup ledger-texi nil - "Options for working on Ledger texi documentation" - :group 'ledger) - -(defcustom ledger-texi-sample-doc-path "~/ledger/doc/sample.dat" - "Location for sample data to be used in texi tests." - :type 'file - :group 'ledger-texi) - -(defcustom ledger-texi-normalization-args "--args-only --columns 80" - "Texi normalization for producing ledger output." - :type 'string - :group 'ledger-texi) - -(defun ledger-update-test () - (interactive) - (goto-char (point-min)) - (let ((command (buffer-substring (point-min) (line-end-position)))) - (re-search-forward "^<<<\n") - (let ((beg (point)) end) - (re-search-forward "^>>>") - (setq end (match-beginning 0)) - (forward-line 1) - (let ((output-beg (point))) - (re-search-forward "^>>>") - (goto-char (match-beginning 0)) - (delete-region output-beg (point)) - (apply #'call-process-region - beg end (expand-file-name "~/Products/ledger/debug/ledger") - nil t nil - "-f" "-" "--args-only" "--columns=80" "--no-color" - (split-string command " ")))))) - -(defun ledger-texi-write-test (name command input output &optional category) - (let ((buf (current-buffer))) - (with-current-buffer (find-file-noselect - (expand-file-name (concat name ".test") category)) - (erase-buffer) - (let ((case-fold-search nil)) - (if (string-match "\\$LEDGER\\s-+" command) - (setq command (replace-match "" t t command))) - (if (string-match " -f \\$\\([-a-z]+\\)" command) - (setq command (replace-match "" t t command)))) - (insert command ?\n) - (insert "<<<" ?\n) - (insert input) - (insert ">>>1" ?\n) - (insert output) - (insert ">>>2" ?\n) - (insert "=== 0" ?\n) - (save-buffer) - (unless (eq buf (current-buffer)) - (kill-buffer (current-buffer)))))) - -(defun ledger-texi-update-test () - (interactive) - (let ((details (ledger-texi-test-details)) - (name (file-name-sans-extension - (file-name-nondirectory (buffer-file-name))))) - (ledger-texi-write-test - name (nth 0 details) - (nth 1 details) - (ledger-texi-invoke-command - (ledger-texi-expand-command - (nth 0 details) - (ledger-texi-write-test-data name (nth 1 details))))))) - -(defun ledger-texi-test-details () - (goto-char (point-min)) - (let ((command (buffer-substring (point) (line-end-position))) - input output) - (re-search-forward "^<<<") - (let ((input-beg (1+ (match-end 0)))) - (re-search-forward "^>>>1") - (let ((output-beg (1+ (match-end 0)))) - (setq input (buffer-substring input-beg (match-beginning 0))) - (re-search-forward "^>>>2") - (setq output (buffer-substring output-beg (match-beginning 0))) - (list command input output))))) - -(defun ledger-texi-expand-command (command data-file) - (if (string-match "\\$LEDGER" command) - (replace-match (format "%s -f \"%s\" %s" ledger-binary-path - data-file ledger-texi-normalization-args) t t command) - (concat (format "%s -f \"%s\" %s " ledger-binary-path - data-file ledger-texi-normalization-args) command))) - -(defun ledger-texi-invoke-command (command) - (with-temp-buffer (shell-command command t (current-buffer)) - (if (= (point-min) (point-max)) - (progn - (push-mark nil t) - (message "Command '%s' yielded no result at %d" command (point)) - (ding)) - (buffer-string)))) - -(defun ledger-texi-write-test-data (name input) - (let ((path (expand-file-name name temporary-file-directory))) - (with-current-buffer (find-file-noselect path) - (erase-buffer) - (insert input) - (save-buffer)) - path)) - -(defun ledger-texi-update-examples () - (interactive) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward "^@c \\(\\(?:sm\\)?ex\\) \\(\\S-+\\): \\(.*\\)" nil t) - (let ((section (match-string 1)) - (example-name (match-string 2)) - (command (match-string 3)) - (data-file ledger-texi-sample-doc-path)) - (goto-char (match-end 0)) - (forward-line) - (when (looking-at "@\\(\\(?:small\\)?example\\)") - (let ((beg (point))) - (re-search-forward "^@end \\(\\(?:small\\)?example\\)") - (delete-region beg (1+ (point))))) - - (when (let ((case-fold-search nil)) - (string-match " -f \\$\\([-a-z]+\\)" command)) - (let ((label (match-string 1 command))) - (setq command (replace-match "" t t command)) - (save-excursion - (goto-char (point-min)) - (search-forward (format "@c data: %s" label)) - (re-search-forward "@\\(\\(?:small\\)?example\\)") - (forward-line) - (let ((beg (point))) - (re-search-forward "@end \\(\\(?:small\\)?example\\)") - (setq data-file (ledger-texi-write-test-data - (format "%s.dat" label) - (buffer-substring-no-properties - beg (match-beginning 0)))))))) - - (let ((section-name (if (string= section "smex") - "smallexample" - "example")) - (output (ledger-texi-invoke-command - (ledger-texi-expand-command command data-file)))) - (insert "@" section-name ?\n output - "@end " section-name ?\n)) - - ;; Update the regression test associated with this example - (ledger-texi-write-test example-name command nil nil - "../test/manual"))))) - -(provide 'ledger-texi) - -;;; ledger-texi.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-texi.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-texi.elc Binary files differ. diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-xact.el b/emacs/elpa/ledger-mode-20240326.1834/ledger-xact.el @@ -1,220 +0,0 @@ -;;; ledger-xact.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) - -;; This file is not part of GNU Emacs. - -;; This 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 2, or (at your option) any later -;; version. -;; -;; This 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 GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -;; MA 02110-1301 USA. - - -;;; Commentary: -;; Utilities for running ledger synchronously. - -;;; Code: - -(require 'eshell) -(require 'ledger-regex) -(require 'ledger-navigate) -(require 'ledger-exec) -(require 'ledger-post) -(declare-function ledger-read-date "ledger-mode" (prompt)) - -;; TODO: This file depends on code in ledger-mode.el, which depends on this. - -(defcustom ledger-highlight-xact-under-point t - "If t highlight xact under point." - :type 'boolean - :group 'ledger) - -(defcustom ledger-add-transaction-prompt-for-text t - "When non-nil, use ledger xact to format transaction. -When nil, `ledger-add-transaction' will not prompt twice." - :type 'boolean - :package-version '(ledger-mode . "4.0.1") - :group 'ledger) - -(defvar-local ledger-xact-highlight-overlay (list)) - -(defun ledger-highlight-make-overlay () - (let ((ovl (make-overlay 1 1))) - (overlay-put ovl 'font-lock-face 'ledger-font-xact-highlight-face) - (overlay-put ovl 'priority '(nil . 99)) - ovl)) - -(defun ledger-highlight-xact-under-point () - "Move the highlight overlay to the current transaction." - (when ledger-highlight-xact-under-point - (unless ledger-xact-highlight-overlay - (setq ledger-xact-highlight-overlay (ledger-highlight-make-overlay))) - (let ((exts (ledger-navigate-find-element-extents (point)))) - (let ((b (car exts)) - (e (cadr exts)) - (p (point))) - (if (and (> (- e b) 1) ; not an empty line - (<= p e) (>= p b) ; point is within the boundaries - (not (region-active-p))) ; no active region - (move-overlay ledger-xact-highlight-overlay b (+ 1 e)) - (move-overlay ledger-xact-highlight-overlay 1 1)))))) - -(defun ledger-xact-context () - "Return the context of the transaction containing point or nil." - (let ((i 0)) - (while (eq (ledger-context-line-type (ledger-context-other-line i)) 'acct-transaction) - (setq i (- i 1))) - (let ((context-info (ledger-context-other-line i))) - (when (eq (ledger-context-line-type context-info) 'xact) - context-info)))) - -(defun ledger-xact-payee () - "Return the payee of the transaction containing point or nil." - (when-let ((xact-context (ledger-xact-context))) - (ledger-context-field-value xact-context 'payee))) - -(defun ledger-xact-date () - "Return the date of the transaction containing point or nil." - (when-let ((xact-context (ledger-xact-context))) - (ledger-context-field-value xact-context 'date))) - -(defun ledger-xact-find-slot (moment) - "Find the right place in the buffer for a transaction at MOMENT. -MOMENT is an encoded date" - (let (last-xact-start) - (catch 'found - (ledger-xact-iterate-transactions - (lambda (start date _mark _desc) - (setq last-xact-start start) - (when (time-less-p moment date) - (throw 'found t))))) - ;; If we are inserting at the end of the buffer, insert an extra newline - (when (and (eobp) last-xact-start) - (let ((end (cadr (ledger-navigate-find-xact-extents last-xact-start)))) - (goto-char end) - (insert "\n") - (forward-line))))) - -(defun ledger-xact-iterate-transactions (callback) - "Iterate through each transaction call CALLBACK for each." - (goto-char (point-min)) - (let* ((now (current-time)) - (current-year (nth 5 (decode-time now)))) - (while (not (eobp)) - (when (looking-at ledger-iterate-regexp) - (if-let ((year (match-string 1))) - (setq current-year (string-to-number year)) ;a Y directive was found - (let ((start (match-beginning 0)) - (year (match-string (+ ledger-regex-iterate-group-actual-date 1))) - (month (string-to-number (match-string (+ ledger-regex-iterate-group-actual-date 2)))) - (day (string-to-number (match-string (+ ledger-regex-iterate-group-actual-date 3)))) - (state (match-string ledger-regex-iterate-group-state)) - (payee (match-string ledger-regex-iterate-group-payee))) - (if (and year (> (length year) 0)) - (setq year (string-to-number year))) - (funcall callback start - (encode-time 0 0 0 day month - (or year current-year)) - state payee)))) - (forward-line)))) - -(defcustom ledger-copy-transaction-insert-blank-line-after nil - "When non-nil, insert a blank line after `ledger-copy-transaction-at-point'." - :type 'boolean - :group 'ledger) - -(defun ledger-copy-transaction-at-point (date) - "Ask for a new DATE and copy the transaction under point to that date. -Leave point on the first amount." - (interactive (list (ledger-read-date "Copy to date: "))) - (let* ((extents (ledger-navigate-find-xact-extents (point))) - (transaction (buffer-substring-no-properties (car extents) (cadr extents))) - (encoded-date (ledger-parse-iso-date date))) - (push-mark) - (ledger-xact-find-slot encoded-date) - (insert transaction - (if (and ledger-copy-transaction-insert-blank-line-after (not (eobp))) - "\n\n" - "\n")) - (beginning-of-line -1) - (ledger-navigate-beginning-of-xact) - (re-search-forward ledger-iso-date-regexp) - (replace-match date) - (ledger-next-amount) - (if (re-search-forward "[-0-9]") - (goto-char (match-beginning 0))))) - -(defun ledger-delete-current-transaction (pos) - "Delete the transaction surrounding POS." - (interactive "d") - (let ((bounds (ledger-navigate-find-xact-extents pos))) - (delete-region (car bounds) (cadr bounds))) - (delete-blank-lines)) - -(defvar ledger-add-transaction-last-date nil - "Last date entered using `ledger-read-transaction'.") - -(defun ledger-read-transaction () - "Read the text of a transaction, which is at least the current date." - (let ((date (ledger-read-date "Date: "))) - (concat date " " - (when ledger-add-transaction-prompt-for-text - (read-string (concat "xact " date ": ") nil 'ledger-minibuffer-history))))) - -(defun ledger-parse-iso-date (date) - "Try to parse DATE using `ledger-iso-date-regexp' and return a time value or nil." - (save-match-data - (when (string-match ledger-iso-date-regexp date) - (encode-time 0 0 0 (string-to-number (match-string 4 date)) - (string-to-number (match-string 3 date)) - (string-to-number (match-string 2 date)))))) - -(defun ledger-add-transaction (transaction-text &optional insert-at-point) - "Use ledger xact TRANSACTION-TEXT to add a transaction to the buffer. -If INSERT-AT-POINT is non-nil insert the transaction there, -otherwise call `ledger-xact-find-slot' to insert it at the -correct chronological place in the buffer. - -Interactively, the date is requested via `ledger-read-date' and -the \\[universal-argument] enables INSERT-AT-POINT." - (interactive (list (ledger-read-transaction) current-prefix-arg)) - (let* ((args (with-temp-buffer - (insert transaction-text) - (eshell-parse-arguments (point-min) (point-max)))) - (ledger-buf (current-buffer)) - (separator "\n")) - (unless insert-at-point - (let* ((date (car args)) - (parsed-date (ledger-parse-iso-date date))) - (setq ledger-add-transaction-last-date parsed-date) - (push-mark) - ;; TODO: what about when it can't be parsed? - (ledger-xact-find-slot (or parsed-date date)) - (when (looking-at "\n*\\'") - (setq separator "")))) - (if (cdr args) - (save-excursion - (insert - (with-temp-buffer - (apply #'ledger-exec-ledger ledger-buf (current-buffer) "xact" - (mapcar 'eval args)) - (goto-char (point-min)) - (ledger-post-align-postings (point-min) (point-max)) - (buffer-string)) - separator)) - (insert (car args) " ") - (save-excursion (insert "\n" separator))))) - -(provide 'ledger-xact) - -;;; ledger-xact.el ends here diff --git a/emacs/elpa/ledger-mode-20240326.1834/ledger-xact.elc b/emacs/elpa/ledger-mode-20240326.1834/ledger-xact.elc Binary files differ. diff --git a/emacs/elpa/marginalia-20240323.2015/marginalia-autoloads.el b/emacs/elpa/marginalia-20240323.2015/marginalia-autoloads.el @@ -1,56 +0,0 @@ -;;; marginalia-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 marginalia.el - -(defvar marginalia-mode nil "\ -Non-nil if Marginalia mode is enabled. -See the `marginalia-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `marginalia-mode'.") -(custom-autoload 'marginalia-mode "marginalia" nil) -(autoload 'marginalia-mode "marginalia" "\ -Annotate completion candidates with richer information. - -This is a global minor mode. If called interactively, toggle the -`Marginalia 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 \\='marginalia-mode)'. - -The mode's hook is called both when the mode is enabled and when -it is disabled. - -(fn &optional ARG)" t) -(autoload 'marginalia-cycle "marginalia" "\ -Cycle between annotators in `marginalia-annotator-registry'." t) -(register-definition-prefixes "marginalia" '("marginalia-")) - -;;; End of scraped data - -(provide 'marginalia-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; marginalia-autoloads.el ends here diff --git a/emacs/elpa/marginalia-20240323.2015/marginalia-pkg.el b/emacs/elpa/marginalia-20240323.2015/marginalia-pkg.el @@ -1,15 +0,0 @@ -(define-package "marginalia" "20240323.2015" "Enrich existing commands with completion annotations" - '((emacs "27.1") - (compat "29.1.4.4")) - :commit "3275d1f85cb020280979a050054b843f7563aea2" :authors - '(("Omar Antolín Camarena <omar@matem.unam.mx>, Daniel Mendler" . "mail@daniel-mendler.de")) - :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 - '("docs" "help" "matching" "completion") - :url "https://github.com/minad/marginalia") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/marginalia-20240323.2015/marginalia.el b/emacs/elpa/marginalia-20240323.2015/marginalia.el @@ -1,1361 +0,0 @@ -;;; marginalia.el --- Enrich existing commands with completion annotations -*- lexical-binding: t -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; Author: 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> -;; Created: 2020 -;; Version: 1.5 -;; Package-Requires: ((emacs "27.1") (compat "29.1.4.4")) -;; Homepage: https://github.com/minad/marginalia -;; Keywords: docs, help, matching, completion - -;; 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: - -;; Enrich existing commands with completion annotations - -;;; Code: - -(require 'compat) -(eval-when-compile - (require 'subr-x) - (require 'cl-lib)) - -;;;; Customization - -(defgroup marginalia nil - "Enrich existing commands with completion annotations." - :link '(info-link :tag "Info Manual" "(marginalia)") - :link '(url-link :tag "Homepage" "https://github.com/minad/marginalia") - :link '(emacs-library-link :tag "Library Source" "marginalia.el") - :group 'help - :group 'docs - :group 'minibuffer - :prefix "marginalia-") - -(defcustom marginalia-field-width 80 - "Maximum truncation width of annotation fields. - -This value is adjusted depending on the `window-width'." - :type 'natnum) - -(defcustom marginalia-separator " " - "Annotation field separator." - :type 'string) - -(defcustom marginalia-align 'left - "Alignment of the annotations." - :type '(choice (const :tag "Left" left) - (const :tag "Center" center) - (const :tag "Right" right))) - -(defcustom marginalia-align-offset 0 - "Additional offset added to the alignment." - :type 'natnum) - -(defcustom marginalia-max-relative-age (* 60 60 24 14) - "Maximum relative age in seconds displayed by the file annotator. - -Set to `most-positive-fixnum' to always use a relative age, or 0 to never show -a relative age." - :type 'natnum) - -(defcustom marginalia-remote-file-regexps - '("\\`/\\([^/|:]+\\):") ;; Tramp path - "List of remote file regexps where the files should not be annotated. - -The first match group is displayed instead of the detailed file -attribute information. For Tramp paths, the protocol is -displayed instead." - :type '(repeat regexp)) - -(defcustom marginalia-annotator-registry - (mapcar - (lambda (x) (append x '(builtin none))) - `((command ,#'marginalia-annotate-command ,#'marginalia-annotate-binding) - (embark-keybinding ,#'marginalia-annotate-embark-keybinding) - (customize-group ,#'marginalia-annotate-customize-group) - (variable ,#'marginalia-annotate-variable) - (function ,#'marginalia-annotate-function) - (face ,#'marginalia-annotate-face) - (color ,#'marginalia-annotate-color) - (unicode-name ,#'marginalia-annotate-char) - (minor-mode ,#'marginalia-annotate-minor-mode) - (symbol ,#'marginalia-annotate-symbol) - (environment-variable ,#'marginalia-annotate-environment-variable) - (input-method ,#'marginalia-annotate-input-method) - (coding-system ,#'marginalia-annotate-coding-system) - (charset ,#'marginalia-annotate-charset) - (package ,#'marginalia-annotate-package) - (imenu ,#'marginalia-annotate-imenu) - (bookmark ,#'marginalia-annotate-bookmark) - (file ,#'marginalia-annotate-file) - (project-file ,#'marginalia-annotate-project-file) - (buffer ,#'marginalia-annotate-buffer) - (library ,#'marginalia-annotate-library) - (theme ,#'marginalia-annotate-theme) - (tab ,#'marginalia-annotate-tab) - (multi-category ,#'marginalia-annotate-multi-category))) - "Annotator function registry. -Associates completion categories with annotation functions. -Each annotation function must return a string, -which is appended to the completion candidate." - :type '(alist :key-type symbol :value-type (repeat symbol))) - -(defcustom marginalia-classifiers - (list #'marginalia-classify-by-command-name - #'marginalia-classify-original-category - #'marginalia-classify-by-prompt - #'marginalia-classify-symbol) - "List of functions to determine current completion category. -Each function should take no arguments and return a symbol -indicating the category, or nil to indicate it could not -determine it." - :type 'hook) - -(defcustom marginalia-prompt-categories - '(("\\<customize group\\>" . customize-group) - ("\\<M-x\\>" . command) - ("\\<package\\>" . package) - ("\\<bookmark\\>" . bookmark) - ("\\<color\\>" . color) - ("\\<face\\>" . face) - ("\\<environment variable\\>" . environment-variable) - ("\\<function\\|hook to remove\\>" . function) - ("\\<variable\\>" . variable) - ("\\<input method\\>" . input-method) - ("\\<charset\\>" . charset) - ("\\<coding system\\>" . coding-system) - ("\\<minor mode\\>" . minor-mode) - ("\\<kill-ring\\>" . kill-ring) - ("\\<tab by name\\>" . tab) - ("\\<library\\>" . library) - ("\\<theme\\>" . theme)) - "Associates regexps to match against minibuffer prompts with categories. -The prompts are matched case-insensitively." - :type '(alist :key-type regexp :value-type symbol)) - -(defcustom marginalia-censor-variables - '("pass\\|auth-source-netrc-cache\\|auth-source-.*-nonce\\|api-?key") - "The value of variables matching any of these regular expressions is not shown. -This configuration variable is useful to hide variables which may -hold sensitive data, e.g., passwords. The variable names are -matched case-sensitively." - :type '(repeat (choice symbol regexp))) - -(defcustom marginalia-command-categories - '((imenu . imenu) - (recentf-open . file) - (where-is . command)) - "Associate commands with a completion category. -The value of `this-command' is used as key for the lookup." - :type '(alist :key-type symbol :value-type symbol)) - -(defgroup marginalia-faces nil - "Faces used by `marginalia-mode'." - :group 'marginalia - :group 'faces) - -(defface marginalia-key - '((t :inherit font-lock-keyword-face)) - "Face used to highlight keys.") - -(defface marginalia-type - '((t :inherit marginalia-key)) - "Face used to highlight types.") - -(defface marginalia-char - '((t :inherit marginalia-key)) - "Face used to highlight character annotations.") - -(defface marginalia-lighter - '((t :inherit marginalia-size)) - "Face used to highlight minor mode lighters.") - -(defface marginalia-on - '((t :inherit success)) - "Face used to signal enabled modes.") - -(defface marginalia-off - '((t :inherit error)) - "Face used to signal disabled modes.") - -(defface marginalia-documentation - '((t :inherit completions-annotations)) - "Face used to highlight documentation strings.") - -(defface marginalia-value - '((t :inherit marginalia-key)) - "Face used to highlight general variable values.") - -(defface marginalia-null - '((t :inherit font-lock-comment-face)) - "Face used to highlight null or unbound variable values.") - -(defface marginalia-true - '((t :inherit font-lock-builtin-face)) - "Face used to highlight true variable values.") - -(defface marginalia-function - '((t :inherit font-lock-function-name-face)) - "Face used to highlight function symbols.") - -(defface marginalia-symbol - '((t :inherit font-lock-type-face)) - "Face used to highlight general symbols.") - -(defface marginalia-list - '((t :inherit font-lock-constant-face)) - "Face used to highlight list expressions.") - -(defface marginalia-mode - '((t :inherit marginalia-key)) - "Face used to highlight buffer major modes.") - -(defface marginalia-date - '((t :inherit marginalia-key)) - "Face used to highlight dates.") - -(defface marginalia-version - '((t :inherit marginalia-number)) - "Face used to highlight package versions.") - -(defface marginalia-archive - '((t :inherit warning)) - "Face used to highlight package archives.") - -(defface marginalia-installed - '((t :inherit success)) - "Face used to highlight the status of packages.") - -(defface marginalia-size - '((t :inherit marginalia-number)) - "Face used to highlight sizes.") - -(defface marginalia-number - '((t :inherit font-lock-constant-face)) - "Face used to highlight numeric values.") - -(defface marginalia-string - '((t :inherit font-lock-string-face)) - "Face used to highlight string values.") - -(defface marginalia-modified - '((t :inherit font-lock-negation-char-face)) - "Face used to highlight buffer modification indicators.") - -(defface marginalia-file-name - '((t :inherit marginalia-documentation)) - "Face used to highlight file names.") - -(defface marginalia-file-owner - '((t :inherit font-lock-preprocessor-face)) - "Face used to highlight file owner and group names.") - -(defface marginalia-file-priv-no - '((t :inherit shadow)) - "Face used to highlight the no file privilege attribute.") - -(defface marginalia-file-priv-dir - '((t :inherit font-lock-keyword-face)) - "Face used to highlight the dir file privilege attribute.") - -(defface marginalia-file-priv-link - '((t :inherit font-lock-keyword-face)) - "Face used to highlight the link file privilege attribute.") - -(defface marginalia-file-priv-read - '((t :inherit font-lock-type-face)) - "Face used to highlight the read file privilege attribute.") - -(defface marginalia-file-priv-write - '((t :inherit font-lock-builtin-face)) - "Face used to highlight the write file privilege attribute.") - -(defface marginalia-file-priv-exec - '((t :inherit font-lock-function-name-face)) - "Face used to highlight the exec file privilege attribute.") - -(defface marginalia-file-priv-other - '((t :inherit font-lock-constant-face)) - "Face used to highlight some other file privilege attribute.") - -(defface marginalia-file-priv-rare - '((t :inherit font-lock-variable-name-face)) - "Face used to highlight a rare file privilege attribute.") - -;;;; Pre-declarations for external packages - -(declare-function bookmark-prop-get "bookmark") - -(declare-function project-current "project") - -(defvar package--builtins) -(defvar package-archive-contents) -(declare-function package--from-builtin "package") -(declare-function package-desc-archive "package") -(declare-function package-desc-status "package") -(declare-function package-desc-summary "package") -(declare-function package-desc-version "package") -(declare-function package-version-join "package") - -(declare-function color-rgb-to-hex "color") -(declare-function color-rgb-to-hsl "color") -(declare-function color-hsl-to-rgb "color") - -;;;; Marginalia mode - -(defvar marginalia--pangram "Cwm fjord bank glyphs vext quiz.") - -(defvar marginalia--bookmark-type-transforms - (let ((words (regexp-opt '("handle" "handler" "jump" "bookmark")))) - `((,(format "-+%s-+" words) . "-") - (,(format "\\`%s-+" words) . "") - (,(format "-%s\\'" words) . "") - ("\\`default\\'" . "File") - (".*" . ,#'capitalize))) - "List of bookmark type transformers. -Relying on this mechanism is discouraged in favor of the -`bookmark-handler-type' property. The function names are matched -case-sensitively.") - -(defvar marginalia--cand-width-step 10 - "Round candidate width.") - -(defvar-local marginalia--cand-width-max 20 - "Maximum width of candidates.") - -(defvar marginalia--fontified-file-modes nil - "List of fontified file modes.") - -(defvar-local marginalia--cache nil - "The cache, pair of list and hashtable.") - -(defvar marginalia--cache-size 100 - "Size of the cache, set to 0 to disable the cache. -Disabling the cache is useful on non-incremental UIs like default completion or -for performance profiling of the annotators.") - -(defvar-local marginalia--command nil - "Last command symbol saved in order to allow annotations.") - -(defvar-local marginalia--base-position 0 - "Last completion base position saved to get full file paths.") - -(defvar marginalia--metadata nil - "Completion metadata from the current completion.") - -(defvar marginalia--ellipsis nil) -(defun marginalia--ellipsis () - "Return ellipsis." - (with-memoization marginalia--ellipsis - (cond - ((bound-and-true-p truncate-string-ellipsis)) - ((char-displayable-p ?…) "…") - ("...")))) - -(defun marginalia--truncate (str width) - "Truncate string STR to WIDTH." - (when (floatp width) (setq width (round (* width marginalia-field-width)))) - (when-let (pos (string-search "\n" str)) - (setq str (substring str 0 pos))) - (let* ((face (and (not (equal str "")) - (get-text-property (1- (length str)) 'face str))) - (ell (if face - (propertize (marginalia--ellipsis) 'face face) - (marginalia--ellipsis))) - (trunc - (if (< width 0) - (nreverse (truncate-string-to-width (reverse str) (- width) 0 ?\s ell)) - (truncate-string-to-width str width 0 ?\s ell)))) - (unless (string-prefix-p str trunc) - (put-text-property 0 (length trunc) 'help-echo str trunc)) - trunc)) - -(cl-defmacro marginalia--field (field &key truncate face width format) - "Format FIELD as a string according to some options. -TRUNCATE is the truncation width. -WIDTH is the field width. -FORMAT is a format string. -FACE is the name of the face, with which the field should be propertized." - (setq field (if format `(format ,format ,field) `(or ,field ""))) - (when width (setq field `(format ,(format "%%%ds" (- width)) ,field))) - (when truncate (setq field `(marginalia--truncate ,field ,truncate))) - (when face (setq field `(propertize ,field 'face ,face))) - field) - -(defmacro marginalia--fields (&rest fields) - "Format annotation FIELDS as a string with separators in between." - (let ((left t)) - (cons 'concat - (mapcan - (lambda (field) - (if (not (eq (car field) :left)) - `(,@(when left (setq left nil) `(#(" " 0 1 (marginalia--align t)))) - marginalia-separator (marginalia--field ,@field)) - (unless left (error "Left fields must come first")) - `((marginalia--field ,@(cdr field))))) - fields)))) - -(defmacro marginalia--in-minibuffer (&rest body) - "Run BODY inside minibuffer if minibuffer is active. -Otherwise stay within current buffer." - (declare (indent 0)) - `(with-current-buffer (if-let (win (active-minibuffer-window)) - (window-buffer win) - (current-buffer)) - ,@body)) - -(defun marginalia--documentation (str) - "Format documentation string STR." - (when str - (marginalia--fields - (str :truncate 1.0 :face 'marginalia-documentation)))) - -(defun marginalia-annotate-binding (cand) - "Annotate command CAND with keybinding." - (when-let ((sym (intern-soft cand)) - (key (and (commandp sym) (where-is-internal sym nil 'first-only)))) - (format #(" (%s)" 1 5 (face marginalia-key)) (key-description key)))) - -(defun marginalia--annotator (cat) - "Return annotation function for category CAT." - (pcase (car (alist-get cat marginalia-annotator-registry)) - ('none #'ignore) - ('builtin nil) - (fun fun))) - -(defun marginalia-annotate-multi-category (cand) - "Annotate multi-category CAND with the buffer class." - (if-let ((multi (get-text-property 0 'multi-category cand)) - (annotate (marginalia--annotator (car multi)))) - ;; Use the Marginalia annotator corresponding to the multi category. - (funcall annotate (cdr multi)) - ;; Apply the original annotation function on the original candidate, if - ;; there is one. Use `alist-get' instead of `completion-metadata-get' to - ;; bypass our `marginalia--completion-metadata-get' advice! - (when-let (annotate (alist-get 'annotation-function marginalia--metadata)) - (funcall annotate cand)))) - -(defconst marginalia--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 advice--make-docstring, is this robust? -(defun marginalia--advised (fun) - "Return t if function FUN is advised." - (let ((flist (indirect-function fun))) - (advice--p (if (eq 'macro (car-safe flist)) (cdr flist) flist)))) - -(defun marginalia--symbol-class (s) - "Return symbol class characters for symbol S. - -This function is an extension of `help--symbol-class'. It returns -more fine-grained and more detailed symbol information. - -Function: -f function -c command -C interactive-only command -m macro -F special-form -M module function -P primitive -g cl-generic -p pure -s side-effect-free -@ autoloaded -! advised -- obsolete -& alias - -Variable: -u custom (U modified compared to global value) -v variable -l local (L modified compared to default value) -- obsolete -& alias - -Other: -a face -t cl-type" - (let ((class - (append - (when (fboundp s) - (list - (cond - ((get s 'pure) '("p" . "pure")) - ((get s 'side-effect-free) '("s" . "side-effect-free"))) - (cond - ((commandp s) - (if (get s 'interactive-only) - '("C" . "interactive-only command") - '("c" . "command"))) - ((cl-generic-p s) '("g" . "cl-generic")) - ((macrop (symbol-function s)) '("m" . "macro")) - ((special-form-p (symbol-function s)) '("F" . "special-form")) - ((subr-primitive-p (symbol-function s)) '("P" . "primitive")) - ((module-function-p (symbol-function s)) '("M" . "module function")) - (t '("f" . "function"))) - (and (autoloadp (symbol-function s)) '("@" . "autoload")) - (and (marginalia--advised s) '("!" . "advised")) - (and (symbolp (symbol-function s)) - (cons "&" (format "alias for `%s'" (symbol-function s)))) - (and (get s 'byte-obsolete-info) '("-" . "obsolete")))) - (when (boundp s) - (list - (when (local-variable-if-set-p s) - (if (ignore-errors - (not (equal (symbol-value s) - (default-value s)))) - '("L" . "local, modified from global") - '("l" . "local, unmodified"))) - (if (custom-variable-p s) - (if (ignore-errors - (not (equal (symbol-value s) - (eval (car (get s 'standard-value)))))) - '("U" . "custom, modified from standard") - '("u" . "custom, unmodified")) - '("v" . "variable")) - (and (not (eq (ignore-errors (indirect-variable s)) s)) - (cons "&" (format "alias for `%s'" (ignore-errors (indirect-variable s))))) - (and (get s 'byte-obsolete-variable) '("-" . "obsolete")))) - (list - (and (facep s) '("a" . "face")) - (and (get s 'cl--class) '("t" . "cl-type")))))) ;; cl-find-class, cl--find-class - (setq class (delq nil class)) - (propertize - (format " %-6s" (mapconcat #'car class "")) - 'help-echo - (mapconcat (pcase-lambda (`(,x . ,y)) (concat x " " y)) class "\n")))) - -(defun marginalia--function-doc (sym) - "Documentation string of function SYM." - (when-let (str (ignore-errors (documentation sym))) - (save-match-data - (if (string-match marginalia--advice-regexp str) - (substring str (match-end 0)) - str)))) - -;; Derived from elisp-get-fnsym-args-string -(defun marginalia--function-args (sym) - "Return function arguments for SYM." - (let ((tmp)) - (elisp-function-argstring - (cond - ((listp (setq tmp (gethash (indirect-function sym) - advertised-signature-table t))) - tmp) - ((setq tmp (help-split-fundoc - (ignore-errors (documentation sym t)) - sym)) - (substitute-command-keys (car tmp))) - ((setq tmp (help-function-arglist sym)) - (and - (if (and (stringp tmp) - (string-search "Arg list not available" tmp)) - ;; A shorter text fits better into the - ;; limited Marginalia space. - "[autoload]" - tmp))))))) - -(defun marginalia-annotate-symbol (cand) - "Annotate symbol CAND with its documentation string." - (when-let (sym (intern-soft cand)) - (marginalia--fields - (:left (marginalia-annotate-binding cand)) - ((marginalia--symbol-class sym) :face 'marginalia-type) - ((if (fboundp sym) (marginalia--function-doc sym) - (cl-loop - for doc in '(variable-documentation - face-documentation - group-documentation) - thereis (ignore-errors (documentation-property sym doc)))) - :truncate 1.0 :face 'marginalia-documentation) - ((abbreviate-file-name (or (symbol-file sym) "")) - :truncate -0.5 :face 'marginalia-file-name)))) - -(defun marginalia-annotate-command (cand) - "Annotate command CAND with its documentation string. -Similar to `marginalia-annotate-symbol', but does not show symbol class." - (when-let (sym (intern-soft cand)) - (concat - (marginalia-annotate-binding cand) - (marginalia--documentation (marginalia--function-doc sym))))) - -(defun marginalia-annotate-embark-keybinding (cand) - "Annotate Embark keybinding CAND with its documentation string. -Similar to `marginalia-annotate-command', but does not show the -keybinding since CAND includes it." - (when-let (cmd (get-text-property 0 'embark-command cand)) - (marginalia--documentation (marginalia--function-doc cmd)))) - -(defun marginalia-annotate-imenu (cand) - "Annotate imenu CAND with its documentation string." - (when (derived-mode-p 'emacs-lisp-mode) - ;; Strip until the last whitespace in order to support flat imenu - (marginalia-annotate-symbol (replace-regexp-in-string "\\`.* " "" cand)))) - -(defun marginalia-annotate-function (cand) - "Annotate function CAND with its documentation string." - (when-let (sym (intern-soft cand)) - (when (fboundp sym) - (marginalia--fields - (:left (marginalia-annotate-binding cand)) - ((marginalia--symbol-class sym) :face 'marginalia-type) - ((marginalia--function-args sym) :face 'marginalia-value - :truncate 0.5) - ((marginalia--function-doc sym) :truncate 1.0 - :face 'marginalia-documentation))))) - -(defun marginalia--variable-value (sym) - "Return the variable value of SYM as string." - (cond - ((not (boundp sym)) - (propertize "#<unbound>" 'face 'marginalia-null)) - ((and marginalia-censor-variables - (let ((name (symbol-name sym)) - case-fold-search) - (cl-loop for r in marginalia-censor-variables - thereis (if (symbolp r) - (eq r sym) - (string-match-p r name))))) - (propertize "*****" - 'face 'marginalia-null - 'help-echo "Hidden due to `marginalia-censor-variables'")) - (t - (let ((val (symbol-value sym))) - (pcase val - ('nil (propertize "nil" 'face 'marginalia-null)) - ('t (propertize "t" 'face 'marginalia-true)) - ((pred keymapp) (propertize "#<keymap>" 'face 'marginalia-value)) - ((pred bool-vector-p) (propertize "#<bool-vector>" 'face 'marginalia-value)) - ((pred hash-table-p) (propertize "#<hash-table>" 'face 'marginalia-value)) - ((pred syntax-table-p) (propertize "#<syntax-table>" 'face 'marginalia-value)) - ;; Emacs bug#53988: abbrev-table-p throws an error - ((and (pred vectorp) (guard (ignore-errors (abbrev-table-p val)))) - (propertize "#<abbrev-table>" 'face 'marginalia-value)) - ((pred char-table-p) (propertize "#<char-table>" 'face 'marginalia-value)) - ;; Emacs 29 comes with callable objects or object closures (OClosures) - ((guard (and (fboundp 'oclosure-type) (oclosure-type val))) - (format (propertize "#<oclosure %s>" 'face 'marginalia-function) - (and (fboundp 'oclosure-type) (oclosure-type val)))) - ((pred byte-code-function-p) (propertize "#<byte-code-function>" 'face 'marginalia-function)) - ((and (pred functionp) (pred symbolp)) - ;; We are not consistent here, values are generally printed - ;; unquoted. But we make an exception for function symbols to visually - ;; distinguish them from symbols. I am not entirely happy with this, - ;; but we should not add quotation to every type. - (format (propertize "#'%s" 'face 'marginalia-function) val)) - ((pred recordp) (format (propertize "#<record %s>" 'face 'marginalia-value) (type-of val))) - ((pred symbolp) (propertize (symbol-name val) 'face 'marginalia-symbol)) - ((pred numberp) (propertize (number-to-string val) 'face 'marginalia-number)) - (_ (let ((print-escape-newlines t) - (print-escape-control-characters t) - ;;(print-escape-multibyte t) - (print-level 3) - (print-length marginalia-field-width)) - (propertize - (replace-regexp-in-string - ;; `print-escape-control-characters' does not escape Unicode control characters. - "[\x0-\x1F\x7f-\x9f\x061c\x200e\x200f\x202a-\x202e\x2066-\x2069]" - (lambda (x) (format "\\x%x" (string-to-char x))) - (prin1-to-string - (if (stringp val) - ;; Get rid of string properties to save some of the precious space - (substring-no-properties - val 0 - (min (length val) marginalia-field-width)) - val)) - 'fixedcase 'literal) - 'face - (cond - ((listp val) 'marginalia-list) - ((stringp val) 'marginalia-string) - (t 'marginalia-value)))))))))) - -(defun marginalia-annotate-variable (cand) - "Annotate variable CAND with its documentation string." - (when-let (sym (intern-soft cand)) - (marginalia--fields - ((marginalia--symbol-class sym) :face 'marginalia-type) - ((marginalia--variable-value sym) :truncate 0.5) - ((documentation-property sym 'variable-documentation) - :truncate 1.0 :face 'marginalia-documentation)))) - -(defun marginalia-annotate-environment-variable (cand) - "Annotate environment variable CAND with its current value." - (when-let (val (getenv cand)) - (marginalia--fields - (val :truncate 1.0 :face 'marginalia-value)))) - -(defun marginalia-annotate-face (cand) - "Annotate face CAND with its documentation string and face example." - (when-let (sym (intern-soft cand)) - (marginalia--fields - ;; HACK: Manual alignment to fix misalignment due to face - ((concat marginalia--pangram #(" " 0 1 (display (space :align-to center)))) - :face sym) - ((documentation-property sym 'face-documentation) - :truncate 1.0 :face 'marginalia-documentation)))) - -(defun marginalia-annotate-color (cand) - "Annotate face CAND with its documentation string and face example." - (when-let (rgb (color-name-to-rgb cand)) - (pcase-let* ((`(,r ,g ,b) rgb) - (`(,h ,s ,l) (apply #'color-rgb-to-hsl rgb)) - (cr (color-rgb-to-hex r 0 0)) - (cg (color-rgb-to-hex 0 g 0)) - (cb (color-rgb-to-hex 0 0 b)) - (ch (apply #'color-rgb-to-hex (color-hsl-to-rgb h 1 0.5))) - (cs (apply #'color-rgb-to-hex (color-hsl-to-rgb h s 0.5))) - (cl (apply #'color-rgb-to-hex (color-hsl-to-rgb 0 0 l)))) - (marginalia--fields - (" " :face `(:background ,(apply #'color-rgb-to-hex rgb))) - ((format - "%s%s%s %s" - (propertize "r" 'face `(:background ,cr :foreground ,(readable-foreground-color cr))) - (propertize "g" 'face `(:background ,cg :foreground ,(readable-foreground-color cg))) - (propertize "b" 'face `(:background ,cb :foreground ,(readable-foreground-color cb))) - (color-rgb-to-hex r g b 2))) - ((format - "%s%s%s %3s° %3s%% %3s%%" - (propertize "h" 'face `(:background ,ch :foreground ,(readable-foreground-color ch))) - (propertize "s" 'face `(:background ,cs :foreground ,(readable-foreground-color cs))) - (propertize "l" 'face `(:background ,cl :foreground ,(readable-foreground-color cl))) - (round (* 360 h)) - (round (* 100 s)) - (round (* 100 l)))))))) - -(defun marginalia-annotate-char (cand) - "Annotate character CAND with its general character category and character code." - (when-let (char (char-from-name cand t)) - (marginalia--fields - (:left char :format" (%c)" :face 'marginalia-char) - (char :format "%06X" :face 'marginalia-number) - ((char-code-property-description - 'general-category - (get-char-code-property char 'general-category)) - :width 30 :face 'marginalia-documentation)))) - -(defun marginalia-annotate-minor-mode (cand) - "Annotate minor-mode CAND with status and documentation string." - (let* ((sym (intern-soft cand)) - (message-log-max nil) - (mode (if (and sym (boundp sym)) - sym - (lookup-minor-mode-from-indicator cand))) - (lighter (cdr (assq mode minor-mode-alist))) - (lighter-str (and lighter (string-trim (format-mode-line (cons t lighter)))))) - (marginalia--fields - ((if (and (boundp mode) (symbol-value mode)) - (propertize "On" 'face 'marginalia-on) - (propertize "Off" 'face 'marginalia-off)) :width 3) - ((if (local-variable-if-set-p mode) "Local" "Global") :width 6 :face 'marginalia-type) - (lighter-str :width 20 :face 'marginalia-lighter) - ((marginalia--function-doc mode) - :truncate 1.0 :face 'marginalia-documentation)))) - -(defun marginalia-annotate-package (cand) - "Annotate package CAND with its description summary." - (when-let ((pkg-alist (bound-and-true-p package-alist)) - (name (replace-regexp-in-string "-[0-9\\.-]+\\'" "" cand)) - (pkg (intern-soft name)) - (desc (or (unless (equal name cand) - (cl-loop with version = (substring cand (1+ (length name))) - for d in (alist-get pkg pkg-alist) - if (equal (package-version-join (package-desc-version d)) version) - return d)) - ;; taken from `describe-package-1' - (car (alist-get pkg pkg-alist)) - (if-let (built-in (assq pkg package--builtins)) - (package--from-builtin built-in) - (car (alist-get pkg package-archive-contents)))))) - (marginalia--fields - ((package-version-join (package-desc-version desc)) :truncate 16 :face 'marginalia-version) - ((cond - ((package-desc-archive desc) (propertize (package-desc-archive desc) 'face 'marginalia-archive)) - (t (propertize (or (package-desc-status desc) "orphan") 'face 'marginalia-installed))) :truncate 12) - ((package-desc-summary desc) :truncate 1.0 :face 'marginalia-documentation)))) - -(defun marginalia--bookmark-type (bm) - "Return bookmark type string of BM. -The string is transformed according to `marginalia--bookmark-type-transforms'." - (let ((handler (or (bookmark-prop-get bm 'handler) 'bookmark-default-handler))) - (and - ;; Some libraries use lambda handlers instead of symbols. For - ;; example the function `xwidget-webkit-bookmark-make-record' is - ;; affected. I consider this bad style since then the lambda is - ;; persisted. - (symbolp handler) - (or (get handler 'bookmark-handler-type) - (let ((str (symbol-name handler)) - case-fold-search) - (dolist (transformer marginalia--bookmark-type-transforms str) - (when (string-match-p (car transformer) str) - (setq str - (if (stringp (cdr transformer)) - (replace-regexp-in-string (car transformer) (cdr transformer) str) - (funcall (cdr transformer) str)))))))))) - -(defun marginalia-annotate-bookmark (cand) - "Annotate bookmark CAND with its file name and front context string." - (when-let ((bm (assoc cand (bound-and-true-p bookmark-alist)))) - (marginalia--fields - ((marginalia--bookmark-type bm) :width 10 :face 'marginalia-type) - ((or (bookmark-prop-get bm 'filename) - (bookmark-prop-get bm 'location)) - :truncate (if (bookmark-prop-get bm 'filename) -0.5 0.5) - :face 'marginalia-file-name) - ((let ((front (or (bookmark-prop-get bm 'front-context-string) "")) - (rear (or (bookmark-prop-get bm 'rear-context-string) ""))) - (unless (and (string-blank-p front) (string-blank-p rear)) - (string-clean-whitespace - (concat front (marginalia--ellipsis) rear)))) - :truncate 0.5 :face 'marginalia-documentation)))) - -(defun marginalia-annotate-customize-group (cand) - "Annotate customization group CAND with its documentation string." - (marginalia--documentation (documentation-property (intern cand) 'group-documentation))) - -(defun marginalia-annotate-input-method (cand) - "Annotate input method CAND with its description." - (marginalia--documentation (nth 4 (assoc cand input-method-alist)))) - -(defun marginalia-annotate-charset (cand) - "Annotate charset CAND with its description." - (marginalia--documentation (charset-description (intern cand)))) - -(defun marginalia-annotate-coding-system (cand) - "Annotate coding system CAND with its description." - (marginalia--documentation (coding-system-doc-string (intern cand)))) - -(defun marginalia--buffer-status (buffer) - "Return the status of BUFFER as a string." - (format-mode-line '((:propertize "%1*%1+%1@" face marginalia-modified) - marginalia-separator - (7 (:propertize "%I" face marginalia-size)) - marginalia-separator - ;; InactiveMinibuffer has 18 letters, but there are longer names. - ;; For example Org-Agenda produces very long mode names. - ;; Therefore we have to truncate. - (20 (-20 (:propertize mode-name face marginalia-mode)))) - nil nil buffer)) - -(defun marginalia--buffer-file (buffer) - "Return the file or process name of BUFFER." - (if-let (proc (get-buffer-process buffer)) - (format "(%s %s) %s" - proc (process-status proc) - (abbreviate-file-name (buffer-local-value 'default-directory buffer))) - (abbreviate-file-name - (or (cond - ;; see ibuffer-buffer-file-name - ((buffer-file-name buffer)) - ((when-let (dir (and (local-variable-p 'dired-directory buffer) - (buffer-local-value 'dired-directory buffer))) - (expand-file-name (if (stringp dir) dir (car dir)) - (buffer-local-value 'default-directory buffer)))) - ((local-variable-p 'list-buffers-directory buffer) - (buffer-local-value 'list-buffers-directory buffer))) - "")))) - -(defun marginalia-annotate-buffer (cand) - "Annotate buffer CAND with modification status, file name and major mode." - (when-let ((buffer (get-buffer cand))) - (if (buffer-live-p buffer) - (marginalia--fields - ((marginalia--buffer-status buffer)) - ((marginalia--buffer-file buffer) - :truncate -0.5 :face 'marginalia-file-name)) - (marginalia--fields ("(dead buffer)" :face 'error))))) - -(defun marginalia--full-candidate (cand) - "Return completion candidate CAND in full. -For some completion tables, the completion candidates offered are -meant to be only a part of the full minibuffer contents. For -example, during file name completion the candidates are one path -component of a full file path." - (if-let (win (active-minibuffer-window)) - (with-current-buffer (window-buffer win) - (concat (let ((end (minibuffer-prompt-end))) - (buffer-substring-no-properties - end (+ end marginalia--base-position))) - cand)) - ;; no minibuffer is active, trust that cand already conveys all - ;; necessary information (there's not much else we can do) - cand)) - -(defun marginalia--remote-file-p (file) - "Return non-nil if FILE is remote. -The return value is a string describing the remote location, -e.g., the protocol." - (save-match-data - (setq file (substitute-in-file-name file)) - (cl-loop for r in marginalia-remote-file-regexps - if (string-match r file) - return (or (match-string 1 file) "remote")))) - -(defun marginalia--annotate-local-file (cand) - "Annotate local file CAND." - (marginalia--in-minibuffer - (when-let (attrs (ignore-errors - ;; may throw permission denied errors - (file-attributes (substitute-in-file-name - (marginalia--full-candidate cand)) - 'integer))) - ;; HACK: Format differently accordingly to alignment, since the file owner - ;; is usually not displayed. Otherwise we will see an excessive amount of - ;; whitespace in front of the file permissions. Furthermore the alignment - ;; in `consult-buffer' will look ugly. Find a better solution! - (if (eq marginalia-align 'right) - (marginalia--fields - ;; File owner at the left - ((marginalia--file-owner attrs) :face 'marginalia-file-owner) - ((marginalia--file-modes attrs)) - ((marginalia--file-size attrs) :face 'marginalia-size :width -7) - ((marginalia--time (file-attribute-modification-time attrs)) - :face 'marginalia-date :width -12)) - (marginalia--fields - ((marginalia--file-modes attrs)) - ((marginalia--file-size attrs) :face 'marginalia-size :width -7) - ((marginalia--time (file-attribute-modification-time attrs)) - :face 'marginalia-date :width -12) - ;; File owner at the right - ((marginalia--file-owner attrs) :face 'marginalia-file-owner)))))) - -(defun marginalia-annotate-file (cand) - "Annotate file CAND with its size, modification time and other attributes. -These annotations are skipped for remote paths." - (if-let (remote (or (marginalia--remote-file-p cand) - (when-let (win (active-minibuffer-window)) - (with-current-buffer (window-buffer win) - (marginalia--remote-file-p (minibuffer-contents-no-properties)))))) - (marginalia--fields (remote :format "*%s*" :face 'marginalia-documentation)) - (marginalia--annotate-local-file cand))) - -(defun marginalia--file-owner (attrs) - "Return file owner given ATTRS." - (let ((uid (file-attribute-user-id attrs)) - (gid (file-attribute-group-id attrs))) - (when (or (/= (user-uid) uid) (/= (group-gid) gid)) - (format "%s:%s" - (or (user-login-name uid) uid) - (or (group-name gid) gid))))) - -(defun marginalia--file-size (attrs) - "Return formatted file size given ATTRS." - (propertize (file-size-human-readable (file-attribute-size attrs)) - 'help-echo (number-to-string (file-attribute-size attrs)))) - -(defun marginalia--file-modes (attrs) - "Return fontified file modes given the ATTRS." - ;; Without caching this can a be significant portion of the time - ;; `marginalia-annotate-file' takes to execute. Caching improves performance - ;; by about a factor of 20. - (setq attrs (file-attribute-modes attrs)) - (or (car (member attrs marginalia--fontified-file-modes)) - (progn - (setq attrs (substring attrs)) ;; copy because attrs is about to be modified - (dotimes (i (length attrs)) - (put-text-property - i (1+ i) 'face - (pcase (aref attrs i) - (?- 'marginalia-file-priv-no) - (?d 'marginalia-file-priv-dir) - (?l 'marginalia-file-priv-link) - (?r 'marginalia-file-priv-read) - (?w 'marginalia-file-priv-write) - (?x 'marginalia-file-priv-exec) - ((or ?s ?S ?t ?T) 'marginalia-file-priv-other) - (_ 'marginalia-file-priv-rare)) - attrs)) - (push attrs marginalia--fontified-file-modes) - attrs))) - -(defconst marginalia--time-relative - `((100 "sec" 1) - (,(* 60 100) "min" 60.0) - (,(* 3600 30) "hour" 3600.0) - (,(* 3600 24 400) "day" ,(* 3600.0 24.0)) - (nil "year" ,(* 365.25 24 3600))) - "Formatting used by the function `marginalia--time-relative'.") - -;; Taken from `seconds-to-string'. -(defun marginalia--time-relative (time) - "Format TIME as a relative age." - (setq time (max 0 (float-time (time-since time)))) - (let ((sts marginalia--time-relative) here) - (while (and (car (setq here (pop sts))) (<= (car here) time))) - (setq time (round time (caddr here))) - (format "%s %s%s ago" time (cadr here) (if (= time 1) "" "s")))) - -(defun marginalia--time-absolute (time) - "Format TIME as an absolute age." - (let ((system-time-locale "C")) - (format-time-string - (if (> (decoded-time-year (decode-time (current-time))) - (decoded-time-year (decode-time time))) - " %Y %b %d" - "%b %d %H:%M") - time))) - -(defun marginalia--time (time) - "Format file age TIME, suitably for use in annotations." - (propertize - (if (< (float-time (time-since time)) marginalia-max-relative-age) - (marginalia--time-relative time) - (marginalia--time-absolute time)) - 'help-echo (format-time-string "%Y-%m-%d %T" time))) - -(defvar-local marginalia--project-root 'unset) -(defun marginalia--project-root () - "Return project root." - (marginalia--in-minibuffer - (when (eq marginalia--project-root 'unset) - (setq marginalia--project-root - (or (let ((prompt (minibuffer-prompt)) - case-fold-search) - (and (string-match - "\\`\\(?:Dired\\|Find file\\) in \\(.*\\): \\'" - prompt) - (match-string 1 prompt))) - (when-let (proj (project-current)) - (cond - ((fboundp 'project-root) (project-root proj)) - ((fboundp 'project-roots) (car (project-roots proj)))))))) - marginalia--project-root)) - -(defun marginalia-annotate-project-file (cand) - "Annotate file CAND with its size, modification time and other attributes." - ;; Absolute project directories also report project-file category - (if (file-name-absolute-p cand) - (marginalia-annotate-file cand) - (when-let (root (marginalia--project-root)) - (marginalia-annotate-file (expand-file-name cand root))))) - -(defvar-local marginalia--library-cache nil) -(defun marginalia--library-cache () - "Return hash table from library name to library file." - (marginalia--in-minibuffer - ;; `locate-file' and `locate-library' are bottlenecks for the - ;; annotator. Therefore we compute all the library paths first. - (unless marginalia--library-cache - (setq marginalia--library-cache (make-hash-table :test #'equal)) - (dolist (dir (delete-dups - (reverse ;; Reverse because of shadowing - (append load-path (custom-theme--load-path))))) ;; Include themes - (dolist (file (ignore-errors - (directory-files dir 'full - "\\.el\\(?:\\.gz\\)?\\'"))) - (puthash (marginalia--library-name file) - file marginalia--library-cache)))) - marginalia--library-cache)) - -(defun marginalia--library-name (file) - "Get name of library FILE." - (replace-regexp-in-string "\\(\\.gz\\|\\.elc?\\)+\\'" "" - (file-name-nondirectory file))) - -(defun marginalia--library-doc (file) - "Return library documentation string for FILE." - (let ((doc (get-text-property 0 'marginalia--library-doc file))) - (unless doc - ;; Extract documentation string. We cannot use `lm-summary' here, - ;; since it decompresses the whole file, which is slower. - (setq doc (or (ignore-errors - (let ((shell-file-name "sh") - (shell-command-switch "-c")) - (shell-command-to-string - (format (if (string-suffix-p ".gz" file) - "gzip -c -q -d %s | head -n1" - "head -n1 %s") - (shell-quote-argument file))))) - "")) - (cond - ((string-match "\\`(define-package\\s-+\"\\([^\"]+\\)\"" doc) - (setq doc (format "Generated package description from %s.el" - (match-string 1 doc)))) - ((string-match "\\`;+\\s-*" doc) - (setq doc (substring doc (match-end 0))) - (when (string-match "\\`[^ \t]+\\s-+-+\\s-+" doc) - (setq doc (substring doc (match-end 0)))) - (when (string-match "\\s-*-\\*-" doc) - (setq doc (substring doc 0 (match-beginning 0))))) - (t (setq doc ""))) - ;; Add the documentation string to the cache - (put-text-property 0 1 'marginalia--library-doc doc file)) - doc)) - -(defun marginalia-annotate-theme (cand) - "Annotate theme CAND with documentation and path." - (marginalia-annotate-library (concat cand "-theme"))) - -(defun marginalia-annotate-library (cand) - "Annotate library CAND with documentation and path." - (setq cand (marginalia--library-name cand)) - (when-let (file (gethash cand (marginalia--library-cache))) - (marginalia--fields - ;; Display if the corresponding feature is loaded. - ;; feature/=library file, but better than nothing. - ((when-let (sym (intern-soft cand)) - (when (memq sym features) - (propertize "Loaded" 'face 'marginalia-on))) - :width 8) - ((marginalia--library-doc file) - :truncate 1.0 :face 'marginalia-documentation) - ((abbreviate-file-name (file-name-directory file)) - :truncate -0.5 :face 'marginalia-file-name)))) - -(defun marginalia-annotate-tab (cand) - "Annotate named tab CAND with tab index, window and buffer information." - (when-let ((tabs (funcall tab-bar-tabs-function)) - (index (seq-position - tabs nil - (lambda (tab _) (equal (alist-get 'name tab) cand))))) - (let* ((tab (nth index tabs)) - (ws (alist-get 'ws tab)) - (bufs (window-state-buffers ws))) - ;; When the buffer key is present in the window state it is added in front - ;; of the window buffer list and gets duplicated. - (when (cadr (assq 'buffer ws)) (pop bufs)) - (marginalia--fields - (:left (1+ index) :format " (%s)" :face 'marginalia-key) - ((if (eq (car tab) 'current-tab) - (length (window-list nil 'no-minibuf)) - (length bufs)) - :format "win:%s" :face 'marginalia-size) - ((or (alist-get 'group tab) 'none) - :format "group:%s" :face 'marginalia-type :truncate 20) - ((if (eq (car tab) 'current-tab) - "(current tab)" - (string-join bufs " ")) - :face 'marginalia-documentation))))) - -(defun marginalia-classify-by-command-name () - "Lookup category for current command." - (and marginalia--command - (or (alist-get marginalia--command marginalia-command-categories) - ;; The command can be an alias, e.g., `recentf' -> `recentf-open'. - (when-let ((chain (function-alias-p marginalia--command))) - (alist-get (car (last chain)) marginalia-command-categories))))) - -(defun marginalia-classify-original-category () - "Return original category reported by completion metadata." - ;; Use `alist-get' instead of `completion-metadata-get' to bypass our - ;; `marginalia--completion-metadata-get' advice! - (when-let (cat (alist-get 'category marginalia--metadata)) - ;; Ignore Emacs 28 symbol-help category in order to ensure that the - ;; categories are refined to our categories function and variable. - (and (not (eq cat 'symbol-help)) cat))) - -(defun marginalia-classify-symbol () - "Determine if currently completing symbols." - (when-let (mct minibuffer-completion-table) - (when (or (eq mct 'help--symbol-completion-table) - (obarrayp mct) - (and (not (functionp mct)) (consp mct) (symbolp (car mct)))) ; assume list of symbols - 'symbol))) - -(defun marginalia-classify-by-prompt () - "Determine category by matching regexps against the minibuffer prompt. -This runs through the `marginalia-prompt-categories' alist -looking for a regexp that matches the prompt." - (when-let (prompt (minibuffer-prompt)) - (setq prompt - (replace-regexp-in-string "(.*?default.*?)\\|\\[.*?\\]" "" prompt)) - (cl-loop with case-fold-search = t - for (regexp . category) in marginalia-prompt-categories - when (string-match-p regexp prompt) - return category))) - -(defun marginalia--cache-reset (&rest _) - "Reset the cache." - (setq marginalia--cache (and marginalia--cache (> marginalia--cache-size 0) - (cons nil (make-hash-table :test #'equal - :size marginalia--cache-size))))) - -(defun marginalia--cached (cache fun key) - "Cached application of function FUN with KEY. -The CACHE keeps around the last `marginalia--cache-size' computed -annotations. The cache is mainly useful when scrolling in -completion UIs like Vertico or Icomplete." - (if cache - (let ((ht (cdr cache))) - (or (gethash key ht) - (let ((val (funcall fun key))) - (push key (car cache)) - (puthash key val ht) - (when (>= (hash-table-count ht) marginalia--cache-size) - (let ((end (last (car cache) 2))) - (remhash (cadr end) ht) - (setcdr end nil))) - val))) - (funcall fun key))) - -(defun marginalia--align (cands) - "Align annotations of CANDS according to `marginalia-align'." - (cl-loop - for (cand . ann) in cands do - (when-let (align (text-property-any 0 (length ann) 'marginalia--align t ann)) - (setq marginalia--cand-width-max - (max marginalia--cand-width-max - (* (ceiling (+ (string-width cand) - (compat-call string-width ann 0 align)) - marginalia--cand-width-step) - marginalia--cand-width-step))))) - (cl-loop - for (cand . ann) in cands collect - (progn - (when-let (align (text-property-any 0 (length ann) 'marginalia--align t ann)) - (put-text-property - align (1+ align) 'display - `(space :align-to - ,(pcase-exhaustive marginalia-align - ('center `(+ center ,marginalia-align-offset)) - ('left `(+ left ,(+ marginalia-align-offset marginalia--cand-width-max))) - ('right `(+ right ,(+ marginalia-align-offset 1 - (- (compat-call string-width ann 0 align) - (string-width ann))))))) - ann)) - (list cand "" ann)))) - -(defun marginalia--affixate (metadata annotator cands) - "Affixate CANDS given METADATA and Marginalia ANNOTATOR." - ;; Compute minimum width of windows, which display the minibuffer, including - ;; the miniwindow. In general the computed width corresponds to the full - ;; frame width, since the miniwindow spans the full frame. For example - ;; `vertico-buffer' displays the minibuffer in a separate window. Similarly, - ;; we could detect other types of completion buffers, e.g., Embark Collect or - ;; the default completion buffer, and compute smaller widths. - (let* ((width (cl-loop for win in (get-buffer-window-list) minimize (window-width win))) - (marginalia-field-width (min (/ width 2) marginalia-field-width)) - (marginalia--metadata metadata) - (cache marginalia--cache)) - (marginalia--align - ;; Run the annotators in the original window. `with-selected-window' - ;; is necessary because of `lookup-minor-mode-from-indicator'. - ;; Otherwise it would suffice to only change the current buffer. We - ;; need the `selected-window' fallback for Embark Occur. - (with-selected-window (or (minibuffer-selected-window) (selected-window)) - (cl-loop for cand in cands collect - (let ((ann (or (marginalia--cached cache annotator cand) ""))) - (cons cand (if (string-blank-p ann) "" ann)))))))) - -(defun marginalia--completion-metadata-get (metadata prop) - "Meant as :before-until advice for `completion-metadata-get'. -METADATA is the metadata. -PROP is the property which is looked up." - (pcase prop - ('annotation-function - ;; We do want the advice triggered for `completion-metadata-get'. - (when-let ((cat (completion-metadata-get metadata 'category)) - (annotator (marginalia--annotator cat))) - (lambda (cand) - (let ((ann (caddar (marginalia--affixate metadata annotator (list cand))))) - (and (not (equal ann "")) ann))))) - ('affixation-function - ;; We do want the advice triggered for `completion-metadata-get'. - (when-let ((cat (completion-metadata-get metadata 'category)) - (annotator (marginalia--annotator cat))) - (apply-partially #'marginalia--affixate metadata annotator))) - ('category - ;; Find the completion category by trying each of our classifiers. - ;; Store the metadata for `marginalia-classify-original-category'. - (let ((marginalia--metadata metadata)) - (run-hook-with-args-until-success 'marginalia-classifiers))))) - -(defun marginalia--minibuffer-setup () - "Setup the minibuffer for Marginalia. -Remember `this-command' for `marginalia-classify-by-command-name'." - (setq marginalia--cache t marginalia--command this-command) - ;; Reset cache if window size changes, recompute alignment - (add-hook 'window-state-change-hook #'marginalia--cache-reset nil 'local) - (marginalia--cache-reset)) - -(defun marginalia--base-position (completions) - "Record the base position of COMPLETIONS." - ;; As a small optimization we track the base position only for file - ;; completions, since `marginalia--full-candidate' is currently used only by - ;; the file annotation function. - (when minibuffer-completing-file-name - (let ((base (or (cdr (last completions)) 0))) - (unless (= marginalia--base-position base) - (marginalia--cache-reset) - (setq marginalia--base-position base - marginalia--cand-width-max (default-value 'marginalia--cand-width-max))))) - completions) - -;;;###autoload -(define-minor-mode marginalia-mode - "Annotate completion candidates with richer information." - :global t :group 'marginalia - (if marginalia-mode - (progn - ;; Remember `this-command' in order to select the annotation function. - (add-hook 'minibuffer-setup-hook #'marginalia--minibuffer-setup) - ;; Replace the metadata function. - (advice-add #'completion-metadata-get :before-until #'marginalia--completion-metadata-get) - ;; Record completion base position, for `marginalia--full-candidate' - (advice-add #'completion-all-completions :filter-return #'marginalia--base-position)) - (advice-remove #'completion-all-completions #'marginalia--base-position) - (advice-remove #'completion-metadata-get #'marginalia--completion-metadata-get) - (remove-hook 'minibuffer-setup-hook #'marginalia--minibuffer-setup))) - -;;;###autoload -(defun marginalia-cycle () - "Cycle between annotators in `marginalia-annotator-registry'." - (interactive) - (with-current-buffer (window-buffer - (or (active-minibuffer-window) - (user-error "Marginalia: No active minibuffer"))) - (let* ((end (minibuffer-prompt-end)) - (pt (max 0 (- (point) end))) - (metadata (completion-metadata (buffer-substring-no-properties end (+ end pt)) - minibuffer-completion-table - minibuffer-completion-predicate)) - (cat (or (completion-metadata-get metadata 'category) - (user-error "Marginalia: Unknown completion category"))) - (ann (or (assq cat marginalia-annotator-registry) - (user-error "Marginalia: No annotators found for category `%s'" cat)))) - (marginalia--cache-reset) - (setcdr ann (append (cddr ann) (list (cadr ann)))) - ;; When the builtin annotator is selected and no builtin function is - ;; available, skip to the next annotator. Note that we cannot use - ;; `completion-metadata-get' to access the metadata since we must - ;; bypass the `marginalia--completion-metadata-get' advice. - (when (and (eq (cadr ann) 'builtin) - (not (assq 'annotation-function metadata)) - (not (assq 'affixation-function metadata)) - (not (plist-get completion-extra-properties :annotation-function)) - (not (plist-get completion-extra-properties :affixation-function))) - (setcdr ann (append (cddr ann) (list (cadr ann))))) - (message "Marginalia: Use annotator `%s' for category `%s'" (cadr ann) (car ann))))) - -;; Emacs 28: Only show `marginalia-cycle' in M-x in recursive minibuffers -(put #'marginalia-cycle 'completion-predicate - (lambda (&rest _) (> (minibuffer-depth) 1))) - -(provide 'marginalia) -;;; marginalia.el ends here diff --git a/emacs/elpa/marginalia-20240323.2015/marginalia.elc b/emacs/elpa/marginalia-20240323.2015/marginalia.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/coolj.el b/emacs/elpa/notmuch-20231006.2337/coolj.el @@ -1,145 +0,0 @@ -;;; coolj.el --- automatically wrap long lines -*- lexical-binding: t; coding: utf-8 -*- - -;; Copyright (C) 2000, 2001, 2004-2009 Free Software Foundation, Inc. - -;; Authors: Kai Grossjohann <Kai.Grossjohann@CS.Uni-Dortmund.DE> -;; Alex Schroeder <alex@gnu.org> -;; Chong Yidong <cyd@stupidchicken.com> -;; Maintainer: David Edmondson <dme@dme.org> -;; Keywords: convenience, wp - -;; This file is not 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>. - -;;; Commentary: - -;; This is a simple derivative of some functionality from -;; `longlines.el'. The key difference is that this version will -;; insert a prefix at the head of each wrapped line. The prefix is -;; calculated from the originating long line. - -;; No minor-mode is provided, the caller is expected to call -;; `coolj-wrap-region' to wrap the region of interest. - -;;; Code: - -(defgroup coolj nil - "Wrapping of long lines with prefix." - :group 'fill) - -(defcustom coolj-wrap-follows-window-size t - "Non-nil means wrap text to the window size. -Otherwise respect `fill-column'." - :group 'coolj - :type 'boolean) - -(defcustom coolj-line-prefix-regexp "^\\(>+ ?\\)*" - "Regular expression that matches line prefixes." - :group 'coolj - :type 'regexp) - -(defvar-local coolj-wrap-point nil) - -(defun coolj-determine-prefix () - "Determine the prefix for the current line." - (save-excursion - (beginning-of-line) - (if (re-search-forward coolj-line-prefix-regexp nil t) - (buffer-substring (match-beginning 0) (match-end 0)) - ""))) - -(defun coolj-wrap-buffer () - "Wrap the current buffer." - (coolj-wrap-region (point-min) (point-max))) - -(defun coolj-wrap-region (beg end) - "Wrap each successive line, starting with the line before BEG. -Stop when we reach lines after END that don't need wrapping, or the -end of the buffer." - (setq fill-column (if coolj-wrap-follows-window-size - (window-width) - fill-column)) - (let ((mod (buffer-modified-p))) - (setq coolj-wrap-point (point)) - (goto-char beg) - (forward-line -1) - ;; Two successful coolj-wrap-line's in a row mean successive - ;; lines don't need wrapping. - (while (null (and (coolj-wrap-line) - (or (eobp) - (and (>= (point) end) - (coolj-wrap-line)))))) - (goto-char coolj-wrap-point) - (set-buffer-modified-p mod))) - -(defun coolj-wrap-line () - "If the current line needs to be wrapped, wrap it and return nil. -If wrapping is performed, point remains on the line. If the line does -not need to be wrapped, move point to the next line and return t." - (let ((prefix (coolj-determine-prefix))) - (if (coolj-set-breakpoint prefix) - (progn - (insert-before-markers ?\n) - (backward-char 1) - (delete-char -1) - (forward-char 1) - (insert-before-markers prefix) - nil) - (forward-line 1) - t))) - -(defun coolj-set-breakpoint (prefix) - "Place point where we should break the current line, and return t. -If the line should not be broken, return nil; point remains on the -line." - (move-to-column fill-column) - (and (re-search-forward "[^ ]" (line-end-position) 1) - (> (current-column) fill-column) - ;; This line is too long. Can we break it? - (or (coolj-find-break-backward prefix) - (progn (move-to-column fill-column) - (coolj-find-break-forward))))) - -(defun coolj-find-break-backward (prefix) - "Move point backward to the first available breakpoint and return t. -If no breakpoint is found, return nil." - (let ((end-of-prefix (+ (line-beginning-position) (length prefix)))) - (and (search-backward " " end-of-prefix 1) - (save-excursion - (skip-chars-backward " " end-of-prefix) - (null (bolp))) - (progn (forward-char 1) - (if (and fill-nobreak-predicate - (run-hook-with-args-until-success - 'fill-nobreak-predicate)) - (progn (skip-chars-backward " " end-of-prefix) - (coolj-find-break-backward prefix)) - t))))) - -(defun coolj-find-break-forward () - "Move point forward to the first available breakpoint and return t. -If no break point is found, return nil." - (and (search-forward " " (line-end-position) 1) - (progn (skip-chars-forward " " (line-end-position)) - (null (eolp))) - (if (and fill-nobreak-predicate - (run-hook-with-args-until-success - 'fill-nobreak-predicate)) - (coolj-find-break-forward) - t))) - -(provide 'coolj) - -;;; coolj.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/coolj.elc b/emacs/elpa/notmuch-20231006.2337/coolj.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/make-deps.el b/emacs/elpa/notmuch-20231006.2337/make-deps.el @@ -1,69 +0,0 @@ -;;; make-deps.el --- compute make dependencies for Elisp sources -*- lexical-binding: t -*- -;; -;; Copyright © Austin Clements -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: Austin Clements <aclements@csail.mit.edu> - -;;; Code: - -(defun batch-make-deps () - "Invoke `make-deps' for each file on the command line." - (setq debug-on-error t) - (dolist (file command-line-args-left) - (let ((default-directory command-line-default-directory)) - (find-file-literally file)) - (make-deps command-line-default-directory)) - (kill-emacs)) - -(defun make-deps (&optional dir) - "Print make dependencies for the current buffer. - -This prints make dependencies to `standard-output' based on the -top-level `require' expressions in the current buffer. Paths in -rules will be given relative to DIR, or `default-directory'." - (unless dir - (setq dir default-directory)) - (save-excursion - (goto-char (point-min)) - (condition-case nil - (while t - (let ((form (read (current-buffer)))) - ;; Is it a (require 'x) form? - (when (and (listp form) (= (length form) 2) - (eq (car form) 'require) - (listp (cadr form)) (= (length (cadr form)) 2) - (eq (car (cadr form)) 'quote) - (symbolp (cadr (cadr form)))) - ;; Find the required library - (let* ((name (cadr (cadr form))) - (fname (locate-library (symbol-name name)))) - ;; Is this file and the library in the same directory? - ;; If not, assume it's a system library and don't - ;; bother depending on it. - (when (and fname - (string= (file-name-directory (buffer-file-name)) - (file-name-directory fname))) - ;; Print the dependency - (princ (format "%s.elc: %s.elc\n" - (file-name-sans-extension - (file-relative-name (buffer-file-name) dir)) - (file-name-sans-extension - (file-relative-name fname dir))))))))) - (end-of-file nil)))) - -;;; make-deps.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/make-deps.elc b/emacs/elpa/notmuch-20231006.2337/make-deps.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-address.el b/emacs/elpa/notmuch-20231006.2337/notmuch-address.el @@ -1,436 +0,0 @@ -;;; notmuch-address.el --- address completion with notmuch -*- lexical-binding: t -*- -;; -;; Copyright © David Edmondson -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: David Edmondson <dme@dme.org> - -;;; Code: - -(require 'message) -(require 'notmuch-parser) -(require 'notmuch-lib) -(require 'notmuch-company) - -(declare-function company-manual-begin "company") - -;;; Cache internals - -(defvar notmuch-address-last-harvest 0 - "Time of last address harvest.") - -(defvar notmuch-address-completions (make-hash-table :test 'equal) - "Hash of email addresses for completion during email composition. -This variable is set by calling `notmuch-address-harvest'.") - -(defvar notmuch-address-full-harvest-finished nil - "Whether full completion address harvesting has finished. -Use `notmuch-address--harvest-ready' to access as that will load -a saved hash if necessary (and available).") - -(defun notmuch-address--harvest-ready () - "Return t if there is a full address hash available. - -If the hash is not present it attempts to load a saved hash." - (or notmuch-address-full-harvest-finished - (notmuch-address--load-address-hash))) - -;;; Options - -(defcustom notmuch-address-command 'internal - "Determines how address completion candidates are generated. - -If this is a string, then that string should be an external -program, which must take a single argument (searched string) -and output a list of completion candidates, one per line. - -If this is the symbol `internal', then an implementation is used -that relies on the \"notmuch address\" command, but does not use -any third-party (i.e. \"external\") programs. - -If this is the symbol `as-is', then Notmuch does not modify the -value of `message-completion-alist'. This option has to be set to -this value before `notmuch' is loaded, otherwise the modification -to `message-completion-alist' may already have taken place. This -setting obviously does not prevent `message-completion-alist' -from being modified at all; the user or some third-party package -may still modify it. - -Finally, if this is nil, then address completion is disabled." - :type '(radio - (const :tag "Use internal address completion" internal) - (string :tag "Use external completion command") - (const :tag "Disable address completion" nil) - (const :tag "Use default or third-party mechanism" as-is)) - :group 'notmuch-send - :group 'notmuch-address - :group 'notmuch-external) - -(defcustom notmuch-address-internal-completion '(sent nil) - "Determines how internal address completion generates candidates. - -This should be a list of the form (DIRECTION FILTER), where -DIRECTION is either sent or received and specifies whether the -candidates are searched in messages sent by the user or received -by the user (note received by is much faster), and FILTER is -either nil or a filter-string, such as \"date:1y..\" to append to -the query." - :type '(list :tag "Use internal address completion" - (radio - :tag "Base completion on messages you have" - :value sent - (const :tag "sent (more accurate)" sent) - (const :tag "received (faster)" received)) - (radio :tag "Filter messages used for completion" - (const :tag "Use all messages" nil) - (string :tag "Filter query"))) - ;; We override set so that we can clear the cache when this changes - :set (lambda (symbol value) - (set-default symbol value) - (setq notmuch-address-last-harvest 0) - (setq notmuch-address-completions (clrhash notmuch-address-completions)) - (setq notmuch-address-full-harvest-finished nil)) - :group 'notmuch-send - :group 'notmuch-address - :group 'notmuch-external) - -(defcustom notmuch-address-save-filename nil - "Filename to save the cached completion addresses. - -All the addresses notmuch uses for address completion will be -cached in this file. This has obvious privacy implications so -you should make sure it is not somewhere publicly readable." - :type '(choice (const :tag "Off" nil) - (file :tag "Filename")) - :group 'notmuch-send - :group 'notmuch-address - :group 'notmuch-external) - -(defcustom notmuch-address-selection-function 'notmuch-address-selection-function - "The function to select address from given list. - -The function is called with PROMPT, COLLECTION, and INITIAL-INPUT -as arguments (subset of what `completing-read' can be called -with). While executed the value of `completion-ignore-case' -is t. See documentation of function -`notmuch-address-selection-function' to know how address -selection is made by default." - :type 'function - :group 'notmuch-send - :group 'notmuch-address - :group 'notmuch-external) - -(defcustom notmuch-address-post-completion-functions nil - "Functions called after completing address. - -The completed address is passed as an argument to each function. -Note that this hook will be invoked for completion in headers -matching `notmuch-address-completion-headers-regexp'." - :type 'hook - :group 'notmuch-address - :group 'notmuch-hooks) - -(defcustom notmuch-address-use-company t - "If available, use company mode for address completion." - :type 'boolean - :group 'notmuch-send - :group 'notmuch-address) - -;;; Setup - -(defun notmuch-address-selection-function (prompt collection initial-input) - "Default address selection function: delegate to completing read." - (completing-read - prompt collection nil nil initial-input 'notmuch-address-history)) - -(defvar notmuch-address-completion-headers-regexp - "^\\(Resent-\\)?\\(To\\|B?Cc\\|Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):") - -(defvar notmuch-address-history nil) - -(defun notmuch-address-message-insinuate () - (message "calling notmuch-address-message-insinuate is no longer needed")) - -(defun notmuch-address-setup () - (unless (eq notmuch-address-command 'as-is) - (when (and notmuch-address-use-company - (require 'company nil t)) - (notmuch-company-setup)) - (cl-pushnew (cons notmuch-address-completion-headers-regexp - #'notmuch-address-expand-name) - message-completion-alist :test #'equal))) - -(defun notmuch-address-toggle-internal-completion () - "Toggle use of internal completion for current buffer. - -This overrides the global setting for address completion and -toggles the setting in this buffer." - (interactive) - (if (local-variable-p 'notmuch-address-command) - (kill-local-variable 'notmuch-address-command) - (setq-local notmuch-address-command 'internal)) - (when (boundp 'company-idle-delay) - (if (local-variable-p 'company-idle-delay) - (kill-local-variable 'company-idle-delay) - (setq-local company-idle-delay nil)))) - -;;; Completion - -(defun notmuch-address-matching (substring) - "Returns a list of completion candidates matching SUBSTRING. -The candidates are taken from `notmuch-address-completions'." - (let ((candidates) - (re (regexp-quote substring))) - (maphash (lambda (key _val) - (when (string-match re key) - (push key candidates))) - notmuch-address-completions) - candidates)) - -(defun notmuch-address-options (original) - "Return a list of completion candidates. -Use either elisp-based implementation or older implementation -requiring external commands." - (cond - ((eq notmuch-address-command 'internal) - (unless (notmuch-address--harvest-ready) - ;; First, run quick synchronous harvest based on what the user - ;; entered so far. - (notmuch-address-harvest original t)) - (prog1 (notmuch-address-matching original) - ;; Then start the (potentially long-running) full asynchronous - ;; harvest if necessary. - (notmuch-address-harvest-trigger))) - (t - (notmuch--process-lines notmuch-address-command original)))) - -(defun notmuch-address-expand-name () - (cond - ((and (eq notmuch-address-command 'internal) - notmuch-address-use-company - (bound-and-true-p company-mode)) - (company-manual-begin)) - (notmuch-address-command - (let* ((end (point)) - (beg (save-excursion - (re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*") - (goto-char (match-end 0)) - (point))) - (orig (buffer-substring-no-properties beg end)) - (completion-ignore-case t) - (options (with-temp-message "Looking for completion candidates..." - (notmuch-address-options orig))) - (num-options (length options)) - (chosen (cond - ((eq num-options 0) - nil) - ((eq num-options 1) - (car options)) - (t - (funcall notmuch-address-selection-function - (format "Address (%s matches): " num-options) - options - orig))))) - (if chosen - (progn - (push chosen notmuch-address-history) - (delete-region beg end) - (insert chosen) - (run-hook-with-args 'notmuch-address-post-completion-functions - chosen)) - (message "No matches.") - (ding)))) - (t nil))) - -;;; Harvest - -(defun notmuch-address-harvest-addr (result) - (puthash (plist-get result :name-addr) - t notmuch-address-completions)) - -(defun notmuch-address-harvest-filter (proc string) - (when (buffer-live-p (process-buffer proc)) - (with-current-buffer (process-buffer proc) - (save-excursion - (goto-char (point-max)) - (insert string)) - (notmuch-sexp-parse-partial-list - 'notmuch-address-harvest-addr (process-buffer proc))))) - -(defvar notmuch-address-harvest-procs '(nil . nil) - "The currently running harvests. - -The car is a partial harvest, and the cdr is a full harvest.") - -(defun notmuch-address-harvest (&optional addr-prefix synchronous callback) - "Collect addresses completion candidates. - -It queries the notmuch database for messages sent/received (as -configured with `notmuch-address-command') by the user, collects -destination/source addresses from those messages and stores them -in `notmuch-address-completions'. - -If ADDR-PREFIX is not nil, only messages with to/from addresses -matching ADDR-PREFIX*' are queried. - -Address harvesting may take some time so the address collection runs -asynchronously unless SYNCHRONOUS is t. In case of asynchronous -execution, CALLBACK is called when harvesting finishes." - (let* ((sent (eq (car notmuch-address-internal-completion) 'sent)) - (config-query (cadr notmuch-address-internal-completion)) - (prefix-query (and addr-prefix - (format "%s:%s*" - (if sent "to" "from") - addr-prefix))) - (from-or-to-me-query - (mapconcat (lambda (x) - (concat (if sent "from:" "to:") x)) - (notmuch-user-emails) " or ")) - (query (if (or prefix-query config-query) - (concat (format "(%s)" from-or-to-me-query) - (and prefix-query - (format " and (%s)" prefix-query)) - (and config-query - (format " and (%s)" config-query))) - from-or-to-me-query)) - (args `("address" "--format=sexp" "--format-version=5" - ,(if sent "--output=recipients" "--output=sender") - "--deduplicate=address" - ,query))) - (if synchronous - (mapc #'notmuch-address-harvest-addr - (apply 'notmuch-call-notmuch-sexp args)) - ;; Asynchronous - (let* ((current-proc (if addr-prefix - (car notmuch-address-harvest-procs) - (cdr notmuch-address-harvest-procs))) - (proc-name (format "notmuch-address-%s-harvest" - (if addr-prefix "partial" "full"))) - (proc-buf (concat " *" proc-name "*"))) - ;; Kill any existing process - (when current-proc - (kill-buffer (process-buffer current-proc))) ; this also kills the process - (setq current-proc - (apply 'notmuch-start-notmuch proc-name proc-buf - callback ; process sentinel - args)) - (set-process-filter current-proc 'notmuch-address-harvest-filter) - (set-process-query-on-exit-flag current-proc nil) - (if addr-prefix - (setcar notmuch-address-harvest-procs current-proc) - (setcdr notmuch-address-harvest-procs current-proc))))) - ;; return value - nil) - -(defvar notmuch-address--save-hash-version 1 - "Version format of the save hash.") - -(defun notmuch-address--get-address-hash () - "Return the saved address hash as a plist. - -Returns nil if the save file does not exist, or it does not seem -to be a saved address hash." - (and notmuch-address-save-filename - (condition-case nil - (with-temp-buffer - (insert-file-contents notmuch-address-save-filename) - (let ((name (read (current-buffer))) - (plist (read (current-buffer)))) - ;; We do two simple sanity checks on the loaded file. - ;; We just check a version is specified, not that - ;; it is the current version, as we are allowed to - ;; over-write and a save-file with an older version. - (and (string= name "notmuch-address-hash") - (plist-get plist :version) - plist))) - ;; The error case catches any of the reads failing. - (error nil)))) - -(defun notmuch-address--load-address-hash () - "Read the saved address hash and set the corresponding variables." - (let ((load-plist (notmuch-address--get-address-hash))) - (when (and load-plist - ;; If the user's setting have changed, or the version - ;; has changed, return nil to make sure the new settings - ;; take effect. - (equal (plist-get load-plist :completion-settings) - notmuch-address-internal-completion) - (equal (plist-get load-plist :version) - notmuch-address--save-hash-version)) - (setq notmuch-address-last-harvest (plist-get load-plist :last-harvest)) - (setq notmuch-address-completions (plist-get load-plist :completions)) - (setq notmuch-address-full-harvest-finished t) - ;; Return t to say load was successful. - t))) - -(defun notmuch-address--save-address-hash () - (when notmuch-address-save-filename - (if (or (not (file-exists-p notmuch-address-save-filename)) - ;; The file exists, check it is a file we saved. - (notmuch-address--get-address-hash)) - (with-temp-file notmuch-address-save-filename - (let ((save-plist - (list :version notmuch-address--save-hash-version - :completion-settings notmuch-address-internal-completion - :last-harvest notmuch-address-last-harvest - :completions notmuch-address-completions))) - (print "notmuch-address-hash" (current-buffer)) - (print save-plist (current-buffer)))) - (message "\ -Warning: notmuch-address-save-filename %s exists but doesn't -appear to be an address savefile. Not overwriting." - notmuch-address-save-filename)))) - -(defun notmuch-address-harvest-trigger () - (let ((now (float-time))) - (when (> (- now notmuch-address-last-harvest) 86400) - (setq notmuch-address-last-harvest now) - (notmuch-address-harvest - nil nil - (lambda (_proc event) - ;; If harvest fails, we want to try - ;; again when the trigger is next called. - (if (string= event "finished\n") - (progn - (notmuch-address--save-address-hash) - (setq notmuch-address-full-harvest-finished t)) - (setq notmuch-address-last-harvest 0))))))) - -;;; Standalone completion - -(defun notmuch-address-from-minibuffer (prompt) - (if (not notmuch-address-command) - (read-string prompt) - (let ((rmap (copy-keymap minibuffer-local-map)) - (omap minibuffer-local-map)) - ;; Configure TAB to start completion when executing read-string. - ;; "Original" minibuffer keymap is restored just before calling - ;; notmuch-address-expand-name as it may also use minibuffer-local-map - ;; (completing-read probably does not but if something else is used there). - (define-key rmap (kbd "TAB") (lambda () - (interactive) - (let ((enable-recursive-minibuffers t) - (minibuffer-local-map omap)) - (notmuch-address-expand-name)))) - (let ((minibuffer-local-map rmap)) - (read-string prompt))))) - -;;; _ - -(provide 'notmuch-address) - -;;; notmuch-address.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-address.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-address.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-autoloads.el b/emacs/elpa/notmuch-20231006.2337/notmuch-autoloads.el @@ -1,215 +0,0 @@ -;;; notmuch-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 coolj.el - -(register-definition-prefixes "coolj" '("coolj-")) - - -;;; Generated autoloads from make-deps.el - -(register-definition-prefixes "make-deps" '("batch-make-deps" "make-deps")) - - -;;; Generated autoloads from notmuch.el - -(autoload 'notmuch-search "notmuch" "\ -Display threads matching QUERY in a notmuch-search buffer. - -If QUERY is nil, it is read interactively from the minibuffer. -Other optional parameters are used as follows: - - OLDEST-FIRST: A Boolean controlling the sort order of returned threads - TARGET-THREAD: A thread ID (without the thread: prefix) that will be made - current if it appears in the search results. - TARGET-LINE: The line number to move to if the target thread does not - appear in the search results. - NO-DISPLAY: Do not try to foreground the search results buffer. If it is - already foregrounded i.e. displayed in a window, this has no - effect, meaning the buffer will remain visible. - -When called interactively, this will prompt for a query and use -the configured default sort order. - -(fn &optional QUERY OLDEST-FIRST TARGET-THREAD TARGET-LINE NO-DISPLAY)" t) -(autoload 'notmuch "notmuch" "\ -Run notmuch and display saved searches, known tags, etc." t) -(autoload 'notmuch-cycle-notmuch-buffers "notmuch" "\ -Cycle through any existing notmuch buffers (search, show or hello). - -If the current buffer is the only notmuch buffer, bury it. -If no notmuch buffers exist, run `notmuch'." t) -(register-definition-prefixes "notmuch" '("notmuch-")) - - -;;; Generated autoloads from notmuch-address.el - -(register-definition-prefixes "notmuch-address" '("notmuch-address-")) - - -;;; Generated autoloads from notmuch-company.el - -(autoload 'notmuch-company-setup "notmuch-company") -(autoload 'notmuch-company "notmuch-company" "\ -`company-mode' completion back-end for `notmuch'. - -(fn COMMAND &optional ARG &rest IGNORE)" t) -(register-definition-prefixes "notmuch-company" '("notmuch-company-last-prefix")) - - -;;; Generated autoloads from notmuch-compat.el - -(register-definition-prefixes "notmuch-compat" '("notmuch-")) - - -;;; Generated autoloads from notmuch-crypto.el - -(register-definition-prefixes "notmuch-crypto" '("notmuch-crypto-")) - - -;;; Generated autoloads from notmuch-draft.el - -(register-definition-prefixes "notmuch-draft" '("notmuch-draft-")) - - -;;; Generated autoloads from notmuch-hello.el - -(autoload 'notmuch-hello "notmuch-hello" "\ -Run notmuch and display saved searches, known tags, etc. - -(fn &optional NO-DISPLAY)" t) -(register-definition-prefixes "notmuch-hello" '("notmuch-")) - - -;;; Generated autoloads from notmuch-jump.el - -(autoload 'notmuch-jump-search "notmuch-jump" "\ -Jump to a saved search by shortcut key. - -This prompts for and performs a saved search using the shortcut -keys configured in the :key property of `notmuch-saved-searches'. -Typically these shortcuts are a single key long, so this is a -fast way to jump to a saved search from anywhere in Notmuch." t) -(autoload 'notmuch-jump "notmuch-jump" "\ -Interactively prompt for one of the keys in ACTION-MAP. - -Displays a summary of all bindings in ACTION-MAP in the -minibuffer, reads a key from the minibuffer, and performs the -corresponding action. The prompt can be canceled with C-g or -RET. PROMPT must be a string to use for the prompt. PROMPT -should include a space at the end. - -ACTION-MAP must be a list of triples of the form - (KEY LABEL ACTION) -where KEY is a key binding, LABEL is a string label to display in -the buffer, and ACTION is a nullary function to call. LABEL may -be null, in which case the action will still be bound, but will -not appear in the pop-up buffer. - -(fn ACTION-MAP PROMPT)") -(register-definition-prefixes "notmuch-jump" '("notmuch-jump-")) - - -;;; Generated autoloads from notmuch-lib.el - -(register-definition-prefixes "notmuch-lib" '("notmuch-")) - - -;;; Generated autoloads from notmuch-maildir-fcc.el - -(register-definition-prefixes "notmuch-maildir-fcc" '("notmuch-" "with-temporary-notmuch-message-buffer")) - - -;;; Generated autoloads from notmuch-message.el - -(register-definition-prefixes "notmuch-message" '("notmuch-message-")) - - -;;; Generated autoloads from notmuch-mua.el - -(register-definition-prefixes "notmuch-mua" '("notmuch-")) - - -;;; Generated autoloads from notmuch-parser.el - -(register-definition-prefixes "notmuch-parser" '("notmuch-sexp-")) - - -;;; Generated autoloads from notmuch-print.el - -(register-definition-prefixes "notmuch-print" '("notmuch-print-")) - - -;;; Generated autoloads from notmuch-query.el - -(register-definition-prefixes "notmuch-query" '("notmuch-query-")) - - -;;; Generated autoloads from notmuch-show.el - -(autoload 'notmuch-show "notmuch-show" "\ -Run \"notmuch show\" with the given thread ID and display results. - -ELIDE-TOGGLE, if non-nil, inverts the default elide behavior. - -The optional PARENT-BUFFER is the notmuch-search buffer from -which this notmuch-show command was executed, (so that the -next thread from that buffer can be show when done with this -one). - -The optional QUERY-CONTEXT is a notmuch search term. Only -messages from the thread matching this search term are shown if -non-nil. - -The optional BUFFER-NAME provides the name of the buffer in -which the message thread is shown. If it is nil (which occurs -when the command is called interactively) the argument to the -function is used. - -Returns the buffer containing the messages, or NIL if no messages -matched. - -(fn THREAD-ID &optional ELIDE-TOGGLE PARENT-BUFFER QUERY-CONTEXT BUFFER-NAME)" t) -(register-definition-prefixes "notmuch-show" '("notmuch-" "with-current-notmuch-show-message")) - - -;;; Generated autoloads from notmuch-tag.el - -(register-definition-prefixes "notmuch-tag" '("notmuch-")) - - -;;; Generated autoloads from notmuch-tree.el - -(register-definition-prefixes "notmuch-tree" '("notmuch-")) - - -;;; Generated autoloads from notmuch-wash.el - -(register-definition-prefixes "notmuch-wash" '("notmuch-wash-")) - - -;;; Generated autoloads from rstdoc.el - -(register-definition-prefixes "rstdoc" '("rst")) - -;;; End of scraped data - -(provide 'notmuch-autoloads) - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; no-native-compile: t -;; coding: utf-8-emacs-unix -;; End: - -;;; notmuch-autoloads.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-company.el b/emacs/elpa/notmuch-20231006.2337/notmuch-company.el @@ -1,106 +0,0 @@ -;;; notmuch-company.el --- Mail address completion for notmuch via company-mode -*- lexical-binding: t -*- -;; -;; Copyright © Trevor Jim -;; Copyright © Michal Sojka -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: Trevor Jim <tjim@mac.com> -;; Michal Sojka <sojkam1@fel.cvut.cz> -;; Keywords: mail, completion - -;;; Commentary: - -;; Mail address completion for notmuch via company-mode. To enable -;; this, install company mode from <https://company-mode.github.io/>. -;; -;; NB company-minimum-prefix-length defaults to 3 so you don't get -;; completion unless you type 3 characters. - -;;; Code: - -(require 'notmuch-lib) - -(defvar-local notmuch-company-last-prefix nil) - -(declare-function company-begin-backend "company") -(declare-function company-grab "company") -(declare-function company-mode "company") -(declare-function company-manual-begin "company") -(defvar company-backends) -(defvar company-idle-delay) - -(declare-function notmuch-address-harvest "notmuch-address") -(declare-function notmuch-address-harvest-trigger "notmuch-address") -(declare-function notmuch-address-matching "notmuch-address") -(declare-function notmuch-address--harvest-ready "notmuch-address") -(defvar notmuch-address-completion-headers-regexp) -(defvar notmuch-address-command) - -;;;###autoload -(defun notmuch-company-setup () - (company-mode) - (setq-local company-backends '(notmuch-company)) - ;; Disable automatic company completion unless an internal - ;; completion method is configured. Company completion (using - ;; internal completion) can still be accessed via standard company - ;; functions, e.g., company-complete. - (unless (eq notmuch-address-command 'internal) - (setq-local company-idle-delay nil))) - -;;;###autoload -(defun notmuch-company (command &optional arg &rest _ignore) - "`company-mode' completion back-end for `notmuch'." - (interactive (list 'interactive)) - (require 'company) - (let ((case-fold-search t) - (completion-ignore-case t)) - (cl-case command - (interactive (company-begin-backend 'notmuch-company)) - (prefix (and (or (derived-mode-p 'message-mode) - (derived-mode-p 'org-msg-edit-mode)) - (looking-back - (concat notmuch-address-completion-headers-regexp ".*") - (line-beginning-position)) - (setq notmuch-company-last-prefix - (company-grab "[:,][ \t]*\\(.*\\)" 1 (point-at-bol))))) - (candidates (cond - ((notmuch-address--harvest-ready) - ;; Update harvested addressed from time to time - (notmuch-address-harvest-trigger) - (notmuch-address-matching arg)) - (t - (cons :async - (lambda (callback) - ;; First run quick asynchronous harvest - ;; based on what the user entered so far - (notmuch-address-harvest - arg nil - (lambda (_proc _event) - (funcall callback (notmuch-address-matching arg)) - ;; Then start the (potentially long-running) - ;; full asynchronous harvest if necessary - (notmuch-address-harvest-trigger)))))))) - (match (if (string-match notmuch-company-last-prefix arg) - (match-end 0) - 0)) - (post-completion - (run-hook-with-args 'notmuch-address-post-completion-functions arg)) - (no-cache t)))) - -(provide 'notmuch-company) - -;;; notmuch-company.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-company.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-company.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-compat.el b/emacs/elpa/notmuch-20231006.2337/notmuch-compat.el @@ -1,58 +0,0 @@ -;;; notmuch-compat.el --- compatibility functions for earlier versions of emacs -*- lexical-binding: t -*- -;; -;; The functions in this file are copied from more modern versions of -;; emacs and are Copyright (C) 1985-1986, 1992, 1994-1995, 1999-2017 -;; Free Software Foundation, Inc. -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. - -;;; Code: - -;; Before Emacs 26.1 lines that are longer than 998 octets were not. -;; folded. Commit 77bbca8c82f6e553c42abbfafca28f55fc995d00 fixed -;; that. Until we drop support for Emacs 25 we have to backport that -;; fix. To avoid interfering with Gnus we only run the hook when -;; called from notmuch-message-mode. - -(declare-function mail-header-fold-field "mail-parse" nil) - -(defun notmuch-message--fold-long-headers () - (when (eq major-mode 'notmuch-message-mode) - (goto-char (point-min)) - (while (not (eobp)) - (when (and (looking-at "[^:]+:") - (> (- (line-end-position) (point)) 998)) - (mail-header-fold-field)) - (forward-line 1)))) - -(unless (fboundp 'message--fold-long-headers) - (add-hook 'message-header-hook 'notmuch-message--fold-long-headers)) - -;; `dlet' isn't available until Emacs 28.1. Below is a copy, with the -;; addition of `with-no-warnings'. -(defmacro notmuch-dlet (binders &rest body) - "Like `let*' but using dynamic scoping." - (declare (indent 1) (debug let)) - `(let (_) - (with-no-warnings ; Quiet "lacks a prefix" warning. - ,@(mapcar (lambda (binder) - `(defvar ,(if (consp binder) (car binder) binder))) - binders)) - (let* ,binders ,@body))) - -(provide 'notmuch-compat) - -;;; notmuch-compat.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-compat.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-compat.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-crypto.el b/emacs/elpa/notmuch-20231006.2337/notmuch-crypto.el @@ -1,272 +0,0 @@ -;;; notmuch-crypto.el --- functions for handling display of cryptographic metadata -*- lexical-binding: t -*- -;; -;; Copyright © Jameson Rollins -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: Jameson Rollins <jrollins@finestructure.net> - -;;; Code: - -(require 'epg) -(require 'notmuch-lib) - -(declare-function notmuch-show-get-message-id "notmuch-show" (&optional bare)) - -;;; Options - -(defcustom notmuch-crypto-process-mime t - "Whether to process cryptographic MIME parts. - -If this variable is non-nil signatures in multipart/signed -messages will be verified and multipart/encrypted parts will be -decrypted. The result of the crypto operation will be displayed -in a specially colored header button at the top of the processed -part. Signed parts will have variously colored headers depending -on the success or failure of the verification process and on the -validity of user ID of the signer. - -The effect of setting this variable can be seen temporarily by -providing a prefix when viewing a signed or encrypted message, or -by providing a prefix when reloading the message in notmuch-show -mode." - :type 'boolean - :package-version '(notmuch . "0.25") - :group 'notmuch-crypto) - -(defcustom notmuch-crypto-get-keys-asynchronously t - "Whether to retrieve openpgp keys asynchronously." - :type 'boolean - :group 'notmuch-crypto) - -(defcustom notmuch-crypto-gpg-program epg-gpg-program - "The gpg executable." - :type 'string - :group 'notmuch-crypto) - -;;; Faces - -(defface notmuch-crypto-part-header - '((((class color) - (background dark)) - (:foreground "LightBlue1")) - (((class color) - (background light)) - (:foreground "blue"))) - "Face used for crypto parts headers." - :group 'notmuch-crypto - :group 'notmuch-faces) - -(defface notmuch-crypto-signature-good - '((t (:background "green" :foreground "black"))) - "Face used for good signatures." - :group 'notmuch-crypto - :group 'notmuch-faces) - -(defface notmuch-crypto-signature-good-key - '((t (:background "orange" :foreground "black"))) - "Face used for good signatures." - :group 'notmuch-crypto - :group 'notmuch-faces) - -(defface notmuch-crypto-signature-bad - '((t (:background "red" :foreground "black"))) - "Face used for bad signatures." - :group 'notmuch-crypto - :group 'notmuch-faces) - -(defface notmuch-crypto-signature-unknown - '((t (:background "red" :foreground "black"))) - "Face used for signatures of unknown status." - :group 'notmuch-crypto - :group 'notmuch-faces) - -(defface notmuch-crypto-decryption - '((t (:background "purple" :foreground "black"))) - "Face used for encryption/decryption status messages." - :group 'notmuch-crypto - :group 'notmuch-faces) - -;;; Functions - -(define-button-type 'notmuch-crypto-status-button-type - 'action (lambda (button) (message "%s" (button-get button 'help-echo))) - 'follow-link t - 'help-echo "Set notmuch-crypto-process-mime to process cryptographic mime parts." - :supertype 'notmuch-button-type) - -(defun notmuch-crypto-insert-sigstatus-button (sigstatus from) - "Insert a button describing the signature status SIGSTATUS sent by user FROM." - (let* ((status (plist-get sigstatus :status)) - (show-button t) - (face 'notmuch-crypto-signature-unknown) - (button-action (lambda (button) (message (button-get button 'help-echo)))) - (keyid (concat "0x" (plist-get sigstatus :keyid))) - label help-msg) - (cond - ((string= status "good") - (let ((fingerprint (concat "0x" (plist-get sigstatus :fingerprint))) - (email-or-userid (or (plist-get sigstatus :email) - (plist-get sigstatus :userid)))) - ;; If email or userid are present, they have full or greater validity. - (setq label (concat "Good signature by key: " fingerprint)) - (setq face 'notmuch-crypto-signature-good-key) - (when email-or-userid - (setq label (concat "Good signature by: " email-or-userid)) - (setq face 'notmuch-crypto-signature-good)) - (setq button-action 'notmuch-crypto-sigstatus-good-callback) - (setq help-msg (concat "Click to list key ID 0x" fingerprint ".")))) - ((string= status "error") - (setq label (concat "Unknown key ID " keyid " or unsupported algorithm")) - (setq button-action 'notmuch-crypto-sigstatus-error-callback) - (setq help-msg (concat "Click to retrieve key ID " keyid - " from key server."))) - ((string= status "bad") - (setq label (concat "Bad signature (claimed key ID " keyid ")")) - (setq face 'notmuch-crypto-signature-bad)) - (status - (setq label (concat "Unknown signature status: " status))) - (t - (setq show-button nil))) - (when show-button - (insert-button - (concat "[ " label " ]") - :type 'notmuch-crypto-status-button-type - 'help-echo help-msg - 'face face - 'mouse-face face - 'action button-action - :notmuch-sigstatus sigstatus - :notmuch-from from) - (insert "\n")))) - -(defun notmuch-crypto-sigstatus-good-callback (button) - (let* ((id (notmuch-show-get-message-id)) - (sigstatus (button-get button :notmuch-sigstatus)) - (fingerprint (concat "0x" (plist-get sigstatus :fingerprint))) - (buffer (get-buffer-create "*notmuch-crypto-gpg-out*")) - (window (display-buffer buffer))) - (with-selected-window window - (with-current-buffer buffer - (goto-char (point-max)) - (insert (format "-- Key %s in message %s:\n" - fingerprint id)) - (notmuch--call-process notmuch-crypto-gpg-program nil t t - "--batch" "--no-tty" "--list-keys" fingerprint)) - (recenter -1)))) - -(declare-function notmuch-show-refresh-view "notmuch-show" (&optional reset-state)) -(declare-function notmuch-show-get-message-id "notmuch-show" (&optional bare)) - -(defun notmuch-crypto--async-key-sentinel (process _event) - "When the user asks for a GPG key to be retrieved -asynchronously, handle completion of that task. - -If the retrieval is successful, the thread where the retrieval -was initiated is still displayed and the cursor has not moved, -redisplay the thread." - (let ((status (process-status process)) - (exit-status (process-exit-status process)) - (keyid (process-get process :gpg-key-id))) - (when (memq status '(exit signal)) - (message "Getting the GPG key %s asynchronously...%s." - keyid - (if (= exit-status 0) - "completed" - "failed")) - ;; If the original buffer is still alive and point didn't move - ;; (i.e. the user didn't move on or away), refresh the buffer to - ;; show the updated signature status. - (let ((show-buffer (process-get process :notmuch-show-buffer)) - (show-point (process-get process :notmuch-show-point))) - (when (and (bufferp show-buffer) - (buffer-live-p show-buffer) - (= show-point - (with-current-buffer show-buffer - (point)))) - (with-current-buffer show-buffer - (notmuch-show-refresh-view))))))) - -(defun notmuch-crypto--set-button-label (button label) - "Set the text displayed in BUTTON to LABEL." - (save-excursion - (let ((inhibit-read-only t)) - ;; This knows rather too much about how we typically format - ;; buttons. - (goto-char (button-start button)) - (forward-char 2) - (delete-region (point) (- (button-end button) 2)) - (insert label)))) - -(defun notmuch-crypto-sigstatus-error-callback (button) - "When signature validation has failed, try to retrieve the -corresponding key when the status button is pressed." - (let* ((sigstatus (button-get button :notmuch-sigstatus)) - (keyid (concat "0x" (plist-get sigstatus :keyid))) - (buffer (get-buffer-create "*notmuch-crypto-gpg-out*"))) - (if notmuch-crypto-get-keys-asynchronously - (progn - (notmuch-crypto--set-button-label - button (format "Retrieving key %s asynchronously..." keyid)) - (with-current-buffer buffer - (goto-char (point-max)) - (insert (format "--- Retrieving key %s:\n" keyid))) - (let ((p (notmuch--make-process - :name "notmuch GPG key retrieval" - :connection-type 'pipe - :buffer buffer - :stderr buffer - :command (list notmuch-crypto-gpg-program "--recv-keys" keyid) - :sentinel #'notmuch-crypto--async-key-sentinel))) - (process-put p :gpg-key-id keyid) - (process-put p :notmuch-show-buffer (current-buffer)) - (process-put p :notmuch-show-point (point)) - (message "Getting the GPG key %s asynchronously..." keyid))) - (let ((window (display-buffer buffer))) - (with-selected-window window - (with-current-buffer buffer - (goto-char (point-max)) - (insert (format "--- Retrieving key %s:\n" keyid)) - (notmuch--call-process notmuch-crypto-gpg-program nil t t "--recv-keys" keyid) - (insert "\n") - (notmuch--call-process notmuch-crypto-gpg-program nil t t "--list-keys" keyid)) - (recenter -1)) - (notmuch-show-refresh-view))))) - -(defun notmuch-crypto-insert-encstatus-button (encstatus) - "Insert a button describing the encryption status ENCSTATUS." - (insert-button - (concat "[ " - (let ((status (plist-get encstatus :status))) - (cond - ((string= status "good") - "Decryption successful") - ((string= status "bad") - "Decryption error") - (t - (concat "Unknown encryption status" - (and status (concat ": " status)))))) - " ]") - :type 'notmuch-crypto-status-button-type - 'face 'notmuch-crypto-decryption - 'mouse-face 'notmuch-crypto-decryption) - (insert "\n")) - -;;; _ - -(provide 'notmuch-crypto) - -;;; notmuch-crypto.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-crypto.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-crypto.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-draft.el b/emacs/elpa/notmuch-20231006.2337/notmuch-draft.el @@ -1,287 +0,0 @@ -;;; notmuch-draft.el --- functions for postponing and editing drafts -*- lexical-binding: t -*- -;; -;; Copyright © Mark Walters -;; Copyright © David Bremner -;; Copyright © Leo Gaspard -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: Mark Walters <markwalters1009@gmail.com> -;; David Bremner <david@tethera.net> -;; Leo Gaspard <leo@gaspard.io> - -;;; Code: - -(require 'cl-lib) -(require 'pcase) -(require 'subr-x) - -(require 'notmuch-maildir-fcc) -(require 'notmuch-tag) - -(declare-function notmuch-show-get-message-id "notmuch-show" (&optional bare)) -(declare-function notmuch-message-mode "notmuch-mua") - -;;; Options - -(defgroup notmuch-draft nil - "Saving and editing drafts in Notmuch." - :group 'notmuch) - -(defcustom notmuch-draft-tags '("+draft") - "List of tag changes to apply when saving a draft message in the database. - -Tags starting with \"+\" (or not starting with either \"+\" or -\"-\") in the list will be added, and tags starting with \"-\" -will be removed from the message being stored. - -For example, if you wanted to give the message a \"draft\" tag -but not the (normally added by default) \"inbox\" tag, you would -set: - (\"+draft\" \"-inbox\")" - :type '(repeat string) - :group 'notmuch-draft) - -(defcustom notmuch-draft-folder "drafts" - "Folder to save draft messages in. - -This should be specified relative to the root of the notmuch -database. It will be created if necessary." - :type 'string - :group 'notmuch-draft) - -(defcustom notmuch-draft-quoted-tags '() - "Mml tags to quote. - -This should be a list of mml tags to quote before saving. You do -not need to include \"secure\" as that is handled separately. - -If you include \"part\" then attachments will not be saved with -the draft -- if not then they will be saved with the draft. The -former means the attachments may not still exist when you resume -the message, the latter means that the attachments as they were -when you postponed will be sent with the resumed message. - -Note you may get strange results if you change this between -postponing and resuming a message." - :type '(repeat string) - :group 'notmuch-send) - -(defcustom notmuch-draft-save-plaintext 'ask - "Whether to allow saving plaintext when it seems encryption is intended. -When a message contains mml tags, then that suggest it is -intended to be encrypted. If the user requests that such a -message is saved locally, then this option controls whether -that is allowed. Beside a boolean, this can also be `ask'." - :type '(radio - (const :tag "Never" nil) - (const :tag "Ask every time" ask) - (const :tag "Always" t)) - :group 'notmuch-draft - :group 'notmuch-crypto) - -;;; Internal - -(defvar notmuch-draft-encryption-tag-regex - "<#\\(part encrypt\\|secure.*mode=.*encrypt>\\)" - "Regular expression matching mml tags indicating encryption of part or message.") - -(defvar-local notmuch-draft-id nil - "Message-id of the most recent saved draft of this message.") - -(defun notmuch-draft--mark-deleted () - "Tag the last saved draft deleted. - -Used when a new version is saved, or the message is sent." - (when notmuch-draft-id - (notmuch-tag notmuch-draft-id '("+deleted")))) - -(defun notmuch-draft-quote-some-mml () - "Quote the mml tags in `notmuch-draft-quoted-tags'." - (save-excursion - ;; First we deal with any secure tag separately. - (message-goto-body) - (when (looking-at "<#secure[^\n]*>\n") - (let ((secure-tag (match-string 0))) - (delete-region (match-beginning 0) (match-end 0)) - (message-add-header (concat "X-Notmuch-Emacs-Secure: " secure-tag)))) - ;; This is copied from mml-quote-region but only quotes the - ;; specified tags. - (when notmuch-draft-quoted-tags - (let ((re (concat "<#!*/?\\(" - (mapconcat 'regexp-quote notmuch-draft-quoted-tags "\\|") - "\\)"))) - (message-goto-body) - (while (re-search-forward re nil t) - ;; Insert ! after the #. - (goto-char (+ (match-beginning 0) 2)) - (insert "!")))))) - -(defun notmuch-draft-unquote-some-mml () - "Unquote the mml tags in `notmuch-draft-quoted-tags'." - (save-excursion - (when notmuch-draft-quoted-tags - (let ((re (concat "<#!+/?\\(" - (mapconcat 'regexp-quote notmuch-draft-quoted-tags "\\|") - "\\)"))) - (message-goto-body) - (while (re-search-forward re nil t) - ;; Remove one ! from after the #. - (goto-char (+ (match-beginning 0) 2)) - (delete-char 1)))) - (let (secure-tag) - (save-restriction - (message-narrow-to-headers) - (setq secure-tag (message-fetch-field "X-Notmuch-Emacs-Secure" t)) - (message-remove-header "X-Notmuch-Emacs-Secure")) - (message-goto-body) - (when secure-tag - (insert secure-tag "\n"))))) - -(defun notmuch-draft--has-encryption-tag () - "Return non-nil if there is an mml secure tag." - (save-excursion - (message-goto-body) - (re-search-forward notmuch-draft-encryption-tag-regex nil t))) - -(defun notmuch-draft--query-encryption () - "Return non-nil if we should save a message that should be encrypted. - -`notmuch-draft-save-plaintext' controls the behaviour." - (cl-case notmuch-draft-save-plaintext - ((ask) - (unless (yes-or-no-p - "(Customize `notmuch-draft-save-plaintext' to avoid this warning) -This message contains mml tags that suggest it is intended to be encrypted. -Really save and index an unencrypted copy? ") - (error "Save aborted"))) - ((nil) - (error "Refusing to save draft with encryption tags (see `%s')" - 'notmuch-draft-save-plaintext)) - ((t) - (ignore)))) - -(defun notmuch-draft--make-message-id () - ;; message-make-message-id gives the id inside a "<" ">" pair, - ;; but notmuch doesn't want that form, so remove them. - (concat "draft-" (substring (message-make-message-id) 1 -1))) - -;;; Commands - -(defun notmuch-draft-save () - "Save the current draft message in the notmuch database. - -This saves the current message in the database with tags -`notmuch-draft-tags' (in addition to any default tags -applied to newly inserted messages)." - (interactive) - (when (notmuch-draft--has-encryption-tag) - (notmuch-draft--query-encryption)) - (let ((id (notmuch-draft--make-message-id))) - (with-temporary-notmuch-message-buffer - ;; We insert a Date header and a Message-ID header, the former - ;; so that it is easier to search for the message, and the - ;; latter so we have a way of accessing the saved message (for - ;; example to delete it at a later time). We check that the - ;; user has these in `message-deletable-headers' (the default) - ;; as otherwise they are doing something strange and we - ;; shouldn't interfere. Note, since we are doing this in a new - ;; buffer we don't change the version in the compose buffer. - (cond - ((member 'Message-ID message-deletable-headers) - (message-remove-header "Message-ID") - (message-add-header (concat "Message-ID: <" id ">"))) - (t - (message "You have customized emacs so Message-ID is not a %s" - "deletable header, so not changing it") - (setq id nil))) - (cond - ((member 'Date message-deletable-headers) - (message-remove-header "Date") - (message-add-header (concat "Date: " (message-make-date)))) - (t - (message "You have customized emacs so Date is not a deletable %s" - "header, so not changing it"))) - (message-add-header "X-Notmuch-Emacs-Draft: True") - (notmuch-draft-quote-some-mml) - (notmuch-maildir-setup-message-for-saving) - (notmuch-maildir-notmuch-insert-current-buffer - notmuch-draft-folder t notmuch-draft-tags)) - ;; We are now back in the original compose buffer. Note the - ;; function notmuch-call-notmuch-process (called by - ;; notmuch-maildir-notmuch-insert-current-buffer) signals an error - ;; on failure, so to get to this point it must have - ;; succeeded. Also, notmuch-draft-id is still the id of the - ;; previous draft, so it is safe to mark it deleted. - (notmuch-draft--mark-deleted) - (setq notmuch-draft-id (concat "id:" id)) - (set-buffer-modified-p nil))) - -(defun notmuch-draft-postpone () - "Save the draft message in the notmuch database and exit buffer." - (interactive) - (notmuch-draft-save) - (kill-buffer)) - -(defun notmuch-draft-resume (id) - "Resume editing of message with id ID." - ;; Used by command `notmuch-show-resume-message'. - (let* ((tags (notmuch--process-lines notmuch-command "search" "--output=tags" - "--exclude=false" id)) - (draft (equal tags (notmuch-update-tags tags notmuch-draft-tags)))) - (when (or draft - (yes-or-no-p "Message does not appear to be a draft: edit as new? ")) - (pop-to-buffer-same-window - (get-buffer-create (concat "*notmuch-draft-" id "*"))) - (setq buffer-read-only nil) - (erase-buffer) - (let ((coding-system-for-read 'no-conversion)) - (notmuch--call-process notmuch-command nil t nil "show" "--format=raw" id)) - (mime-to-mml) - (goto-char (point-min)) - (when (re-search-forward "^$" nil t) - (replace-match mail-header-separator t t)) - ;; Remove the Date and Message-ID headers (unless the user has - ;; explicitly customized emacs to tell us not to) as they will - ;; be replaced when the message is sent. - (save-restriction - (message-narrow-to-headers) - (when (member 'Message-ID message-deletable-headers) - (message-remove-header "Message-ID")) - (when (member 'Date message-deletable-headers) - (message-remove-header "Date")) - (unless draft (notmuch-fcc-header-setup)) - ;; The X-Notmuch-Emacs-Draft header is a more reliable - ;; indication of whether the message really is a draft. - (setq draft (> (message-remove-header "X-Notmuch-Emacs-Draft") 0))) - ;; If the message is not a draft we should not unquote any mml. - (when draft - (notmuch-draft-unquote-some-mml)) - (notmuch-message-mode) - (message-goto-body) - (set-buffer-modified-p nil) - ;; If the resumed message was a draft then set the draft - ;; message-id so that we can delete the current saved draft if the - ;; message is resaved or sent. - (setq notmuch-draft-id (and draft id))))) - -;;; _ - -(add-hook 'message-send-hook 'notmuch-draft--mark-deleted) - -(provide 'notmuch-draft) - -;;; notmuch-draft.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-draft.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-draft.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-hello.el b/emacs/elpa/notmuch-20231006.2337/notmuch-hello.el @@ -1,1015 +0,0 @@ -;;; notmuch-hello.el --- welcome to notmuch, a frontend -*- lexical-binding: t -*- -;; -;; Copyright © David Edmondson -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: David Edmondson <dme@dme.org> - -;;; Code: - -(require 'widget) -(require 'wid-edit) ; For `widget-forward'. - -(require 'notmuch-lib) -(require 'notmuch-mua) - -(declare-function notmuch-search "notmuch" - (&optional query oldest-first target-thread target-line - no-display)) -(declare-function notmuch-poll "notmuch-lib" ()) -(declare-function notmuch-tree "notmuch-tree" - (&optional query query-context target buffer-name - open-target unthreaded parent-buffer oldest-first)) -(declare-function notmuch-unthreaded "notmuch-tree" - (&optional query query-context target buffer-name - open-target)) - - -;;; Options - -(defun notmuch-saved-search-get (saved-search field) - "Get FIELD from SAVED-SEARCH. - -If SAVED-SEARCH is a plist, this is just `plist-get', but for -backwards compatibility, this also deals with the two other -possible formats for SAVED-SEARCH: cons cells (NAME . QUERY) and -lists (NAME QUERY COUNT-QUERY)." - (cond - ((keywordp (car saved-search)) - (plist-get saved-search field)) - ;; It is not a plist so it is an old-style entry. - ((consp (cdr saved-search)) - (pcase-let ((`(,name ,query ,count-query) saved-search)) - (cl-case field - (:name name) - (:query query) - (:count-query count-query) - (t nil)))) - (t - (pcase-let ((`(,name . ,query) saved-search)) - (cl-case field - (:name name) - (:query query) - (t nil)))))) - -(defun notmuch-hello-saved-search-to-plist (saved-search) - "Return a copy of SAVED-SEARCH in plist form. - -If saved search is a plist then just return a copy. In other -cases, for backwards compatibility, convert to plist form and -return that." - (if (keywordp (car saved-search)) - (copy-sequence saved-search) - (let ((fields (list :name :query :count-query)) - plist-search) - (dolist (field fields plist-search) - (let ((string (notmuch-saved-search-get saved-search field))) - (when string - (setq plist-search (append plist-search (list field string))))))))) - -(defun notmuch-hello--saved-searches-to-plist (symbol) - "Extract a saved-search variable into plist form. - -The new style saved search is just a plist, but for backwards -compatibility we use this function to extract old style saved -searches so they still work in customize." - (let ((saved-searches (default-value symbol))) - (mapcar #'notmuch-hello-saved-search-to-plist saved-searches))) - -(define-widget 'notmuch-saved-search-plist 'list - "A single saved search property list." - :tag "Saved Search" - :args '((list :inline t - :format "%v" - (group :format "%v" :inline t - (const :format " Name: " :name) - (string :format "%v")) - (group :format "%v" :inline t - (const :format " Query: " :query) - (string :format "%v"))) - (checklist :inline t - :format "%v" - (group :format "%v" :inline t - (const :format "Shortcut key: " :key) - (key-sequence :format "%v")) - (group :format "%v" :inline t - (const :format "Count-Query: " :count-query) - (string :format "%v")) - (group :format "%v" :inline t - (const :format "" :sort-order) - (choice :tag " Sort Order" - (const :tag "Default" nil) - (const :tag "Oldest-first" oldest-first) - (const :tag "Newest-first" newest-first))) - (group :format "%v" :inline t - (const :format "" :search-type) - (choice :tag " Search Type" - (const :tag "Search mode" nil) - (const :tag "Tree mode" tree) - (const :tag "Unthreaded mode" unthreaded)))))) - -(defcustom notmuch-saved-searches - `((:name "inbox" :query "tag:inbox" :key ,(kbd "i")) - (:name "unread" :query "tag:unread" :key ,(kbd "u")) - (:name "flagged" :query "tag:flagged" :key ,(kbd "f")) - (:name "sent" :query "tag:sent" :key ,(kbd "t")) - (:name "drafts" :query "tag:draft" :key ,(kbd "d")) - (:name "all mail" :query "*" :key ,(kbd "a"))) - "A list of saved searches to display. - -The saved search can be given in 3 forms. The preferred way is as -a plist. Supported properties are - - :name Name of the search (required). - :query Search to run (required). - :key Optional shortcut key for `notmuch-jump-search'. - :count-query Optional extra query to generate the count - shown. If not present then the :query property - is used. - :sort-order Specify the sort order to be used for the search. - Possible values are `oldest-first', `newest-first' - or nil. Nil means use the default sort order. - :search-type Specify whether to run the search in search-mode, - tree mode or unthreaded mode. Set to `tree' to - specify tree mode, \\='unthreaded to specify - unthreaded mode, and set to nil (or anything - except tree and unthreaded) to specify search - mode. - -Other accepted forms are a cons cell of the form (NAME . QUERY) -or a list of the form (NAME QUERY COUNT-QUERY)." - ;; The saved-search format is also used by the all-tags notmuch-hello - ;; section. This section generates its own saved-search list in one of - ;; the latter two forms. - :get 'notmuch-hello--saved-searches-to-plist - :type '(repeat notmuch-saved-search-plist) - :tag "List of Saved Searches" - :group 'notmuch-hello) - -(defcustom notmuch-hello-recent-searches-max 10 - "The number of recent searches to display." - :type 'integer - :group 'notmuch-hello) - -(defcustom notmuch-show-empty-saved-searches nil - "Should saved searches with no messages be listed?" - :type 'boolean - :group 'notmuch-hello) - -(defun notmuch-sort-saved-searches (saved-searches) - "Generate an alphabetically sorted saved searches list." - (sort (copy-sequence saved-searches) - (lambda (a b) - (string< (notmuch-saved-search-get a :name) - (notmuch-saved-search-get b :name))))) - -(defcustom notmuch-saved-search-sort-function nil - "Function used to sort the saved searches for the notmuch-hello view. - -This variable controls how saved searches should be sorted. No -sorting (nil) displays the saved searches in the order they are -stored in `notmuch-saved-searches'. Sort alphabetically sorts the -saved searches in alphabetical order. Custom sort function should -be a function or a lambda expression that takes the saved -searches list as a parameter, and returns a new saved searches -list to be used. For compatibility with the various saved-search -formats it should use notmuch-saved-search-get to access the -fields of the search." - :type '(choice (const :tag "No sorting" nil) - (const :tag "Sort alphabetically" notmuch-sort-saved-searches) - (function :tag "Custom sort function" - :value notmuch-sort-saved-searches)) - :group 'notmuch-hello) - -(defvar notmuch-hello-indent 4 - "How much to indent non-headers.") - -(defimage notmuch-hello-logo ((:type svg :file "notmuch-logo.svg"))) - -(defcustom notmuch-show-logo t - "Should the notmuch logo be shown?" - :type 'boolean - :group 'notmuch-hello) - -(defcustom notmuch-show-all-tags-list nil - "Should all tags be shown in the notmuch-hello view?" - :type 'boolean - :group 'notmuch-hello) - -(defcustom notmuch-hello-tag-list-make-query nil - "Function or string to generate queries for the all tags list. - -This variable controls which query results are shown for each tag -in the \"all tags\" list. If nil, it will use all messages with -that tag. If this is set to a string, it is used as a filter for -messages having that tag (equivalent to \"tag:TAG and (THIS-VARIABLE)\"). -Finally this can be a function that will be called for each tag and -should return a filter for that tag, or nil to hide the tag." - :type '(choice (const :tag "All messages" nil) - (const :tag "Unread messages" "tag:unread") - (string :tag "Custom filter" - :value "tag:unread") - (function :tag "Custom filter function")) - :group 'notmuch-hello) - -(defcustom notmuch-hello-hide-tags nil - "List of tags to be hidden in the \"all tags\"-section." - :type '(repeat string) - :group 'notmuch-hello) - -(defface notmuch-hello-logo-background - '((((class color) - (background dark)) - (:background "#5f5f5f")) - (((class color) - (background light)) - (:background "white"))) - "Background colour for the notmuch logo." - :group 'notmuch-hello - :group 'notmuch-faces) - -(defcustom notmuch-column-control t - "Controls the number of columns for saved searches/tags in notmuch view. - -This variable has three potential sets of values: - -- t: automatically calculate the number of columns possible based - on the tags to be shown and the window width, -- an integer: a lower bound on the number of characters that will - be used to display each column, -- a float: a fraction of the window width that is the lower bound - on the number of characters that should be used for each - column. - -So: -- if you would like two columns of tags, set this to 0.5. -- if you would like a single column of tags, set this to 1.0. -- if you would like tags to be 30 characters wide, set this to - 30. -- if you don't want to worry about all of this nonsense, leave - this set to `t'." - :type '(choice - (const :tag "Automatically calculated" t) - (integer :tag "Number of characters") - (float :tag "Fraction of window")) - :group 'notmuch-hello) - -(defcustom notmuch-hello-thousands-separator " " - "The string used as a thousands separator. - -Typically \",\" in the US and UK and \".\" or \" \" in Europe. -The latter is recommended in the SI/ISO 31-0 standard and by the -International Bureau of Weights and Measures." - :type 'string - :group 'notmuch-hello) - -(defcustom notmuch-hello-mode-hook nil - "Functions called after entering `notmuch-hello-mode'." - :type 'hook - :group 'notmuch-hello - :group 'notmuch-hooks) - -(defcustom notmuch-hello-refresh-hook nil - "Functions called after updating a `notmuch-hello' buffer." - :type 'hook - :group 'notmuch-hello - :group 'notmuch-hooks) - -(defconst notmuch-hello-url "https://notmuchmail.org" - "The `notmuch' web site.") - -(defvar notmuch-hello-custom-section-options - '((:filter (string :tag "Filter for each tag")) - (:filter-count (string :tag "Different filter to generate message counts")) - (:initially-hidden (const :tag "Hide this section on startup" t)) - (:show-empty-searches (const :tag "Show queries with no matching messages" t)) - (:hide-if-empty (const :tag "Hide this section if all queries are empty -\(and not shown by show-empty-searches)" t))) - "Various customization-options for notmuch-hello-tags/query-section.") - -(define-widget 'notmuch-hello-tags-section 'lazy - "Customize-type for notmuch-hello tag-list sections." - :tag "Customized tag-list section (see docstring for details)" - :type - `(list :tag "" - (const :tag "" notmuch-hello-insert-tags-section) - (string :tag "Title for this section") - (plist - :inline t - :options - ,(append notmuch-hello-custom-section-options - '((:hide-tags (repeat :tag "Tags that will be hidden" - string))))))) - -(define-widget 'notmuch-hello-query-section 'lazy - "Customize-type for custom saved-search-like sections" - :tag "Customized queries section (see docstring for details)" - :type - `(list :tag "" - (const :tag "" notmuch-hello-insert-searches) - (string :tag "Title for this section") - (repeat :tag "Queries" - (cons (string :tag "Name") (string :tag "Query"))) - (plist :inline t :options ,notmuch-hello-custom-section-options))) - -(defcustom notmuch-hello-sections - (list #'notmuch-hello-insert-header - #'notmuch-hello-insert-saved-searches - #'notmuch-hello-insert-search - #'notmuch-hello-insert-recent-searches - #'notmuch-hello-insert-alltags - #'notmuch-hello-insert-footer) - "Sections for notmuch-hello. - -The list contains functions which are used to construct sections in -notmuch-hello buffer. When notmuch-hello buffer is constructed, -these functions are run in the order they appear in this list. Each -function produces a section simply by adding content to the current -buffer. A section should not end with an empty line, because a -newline will be inserted after each section by `notmuch-hello'. - -Each function should take no arguments. The return value is -ignored. - -For convenience an element can also be a list of the form (FUNC ARG1 -ARG2 .. ARGN) in which case FUNC will be applied to the rest of the -list. - -A \"Customized tag-list section\" item in the customize-interface -displays a list of all tags, optionally hiding some of them. It -is also possible to filter the list of messages matching each tag -by an additional filter query. Similarly, the count of messages -displayed next to the buttons can be generated by applying a -different filter to the tag query. These filters are also -supported for \"Customized queries section\" items." - :group 'notmuch-hello - :type - '(repeat - (choice (function-item notmuch-hello-insert-header) - (function-item notmuch-hello-insert-saved-searches) - (function-item notmuch-hello-insert-search) - (function-item notmuch-hello-insert-recent-searches) - (function-item notmuch-hello-insert-alltags) - (function-item notmuch-hello-insert-footer) - (function-item notmuch-hello-insert-inbox) - notmuch-hello-tags-section - notmuch-hello-query-section - (function :tag "Custom section")))) - -(defcustom notmuch-hello-auto-refresh t - "Automatically refresh when returning to the notmuch-hello buffer." - :group 'notmuch-hello - :type 'boolean) - -;;; Internal variables - -(defvar notmuch-hello-hidden-sections nil - "List of sections titles whose contents are hidden.") - -(defvar notmuch-hello-first-run t - "True if `notmuch-hello' is run for the first time, set to nil afterwards.") - -;;; Widgets for inserters - -(define-widget 'notmuch-search-item 'item - "A recent search." - :format "%v\n" - :value-create 'notmuch-search-item-value-create) - -(defun notmuch-search-item-value-create (widget) - (let ((value (widget-get widget :value))) - (widget-insert (make-string notmuch-hello-indent ?\s)) - (widget-create 'editable-field - :size (widget-get widget :size) - :parent widget - :action #'notmuch-hello-search - value) - (widget-insert " ") - (widget-create 'push-button - :parent widget - :notify #'notmuch-hello-add-saved-search - "save") - (widget-insert " ") - (widget-create 'push-button - :parent widget - :notify #'notmuch-hello-delete-search-from-history - "del"))) - -(defun notmuch-search-item-field-width () - (max 8 ; Don't let the search boxes be less than 8 characters wide. - (- (window-width) - notmuch-hello-indent ; space at bol - notmuch-hello-indent ; space at eol - 1 ; for the space before the [save] button - 6 ; for the [save] button - 1 ; for the space before the [del] button - 5))) ; for the [del] button - -;;; Widget actions - -(defun notmuch-hello-search (widget &rest _event) - (let ((search (widget-value widget))) - (when search - (setq search (string-trim search)) - (let ((history-delete-duplicates t)) - (add-to-history 'notmuch-search-history search))) - (notmuch-search search notmuch-search-oldest-first))) - -(defun notmuch-hello-add-saved-search (widget &rest _event) - (let ((search (widget-value (widget-get widget :parent))) - (name (completing-read "Name for saved search: " - notmuch-saved-searches))) - ;; If an existing saved search with this name exists, remove it. - (setq notmuch-saved-searches - (cl-loop for elem in notmuch-saved-searches - unless (equal name (notmuch-saved-search-get elem :name)) - collect elem)) - ;; Add the new one. - (customize-save-variable 'notmuch-saved-searches - (add-to-list 'notmuch-saved-searches - (list :name name :query search) t)) - (message "Saved '%s' as '%s'." search name) - (notmuch-hello-update))) - -(defun notmuch-hello-delete-search-from-history (widget &rest _event) - (when (y-or-n-p "Are you sure you want to delete this search? ") - (let ((search (widget-value (widget-get widget :parent)))) - (setq notmuch-search-history - (delete search notmuch-search-history))) - (notmuch-hello-update))) - -;;; Button utilities - -;; `notmuch-hello-query-counts', `notmuch-hello-nice-number' and -;; `notmuch-hello-insert-buttons' are used outside this section. -;; All other functions that are defined in this section are only -;; used by these two functions. - -(defun notmuch-hello-longest-label (searches-alist) - (or (cl-loop for elem in searches-alist - maximize (length (notmuch-saved-search-get elem :name))) - 0)) - -(defun notmuch-hello-reflect-generate-row (ncols nrows row list) - (let ((len (length list))) - (cl-loop for col from 0 to (- ncols 1) - collect (let ((offset (+ (* nrows col) row))) - (if (< offset len) - (nth offset list) - ;; Don't forget to insert an empty slot in the - ;; output matrix if there is no corresponding - ;; value in the input matrix. - nil))))) - -(defun notmuch-hello-reflect (list ncols) - "Reflect a `ncols' wide matrix represented by `list' along the -diagonal." - ;; Not very lispy... - (let ((nrows (ceiling (length list) ncols))) - (cl-loop for row from 0 to (- nrows 1) - append (notmuch-hello-reflect-generate-row ncols nrows row list)))) - -(defun notmuch-hello-widget-search (widget &rest _ignore) - (cl-case (widget-get widget :notmuch-search-type) - (tree - (let ((n (notmuch-search-format-buffer-name (widget-value widget) "tree" t))) - (notmuch-tree (widget-get widget :notmuch-search-terms) - nil nil n nil nil nil - (widget-get widget :notmuch-search-oldest-first)))) - (unthreaded - (let ((n (notmuch-search-format-buffer-name (widget-value widget) - "unthreaded" t))) - (notmuch-unthreaded (widget-get widget :notmuch-search-terms) nil nil n))) - (t - (notmuch-search (widget-get widget :notmuch-search-terms) - (widget-get widget :notmuch-search-oldest-first))))) - -(defun notmuch-saved-search-count (search) - (car (notmuch--process-lines notmuch-command "count" search))) - -(defun notmuch-hello-tags-per-line (widest) - "Determine how many tags to show per line and how wide they -should be. Returns a cons cell `(tags-per-line width)'." - (let ((tags-per-line - (cond - ((integerp notmuch-column-control) - (max 1 - (/ (- (window-width) notmuch-hello-indent) - ;; Count is 9 wide (8 digits plus space), 1 for the space - ;; after the name. - (+ 9 1 (max notmuch-column-control widest))))) - ((floatp notmuch-column-control) - (let* ((available-width (- (window-width) notmuch-hello-indent)) - (proposed-width (max (* available-width notmuch-column-control) - widest))) - (floor available-width proposed-width))) - (t - (max 1 - (/ (- (window-width) notmuch-hello-indent) - ;; Count is 9 wide (8 digits plus space), 1 for the space - ;; after the name. - (+ 9 1 widest))))))) - (cons tags-per-line (/ (max 1 - (- (window-width) notmuch-hello-indent - ;; Count is 9 wide (8 digits plus - ;; space), 1 for the space after the - ;; name. - (* tags-per-line (+ 9 1)))) - tags-per-line)))) - -(defun notmuch-hello-filtered-query (query filter) - "Constructs a query to search all messages matching QUERY and FILTER. - -If FILTER is a string, it is directly used in the returned query. - -If FILTER is a function, it is called with QUERY as a parameter and -the string it returns is used as the query. If nil is returned, -the entry is hidden. - -Otherwise, FILTER is ignored." - (cond - ((functionp filter) (funcall filter query)) - ((stringp filter) - (concat "(" query ") and (" filter ")")) - (t query))) - -(defun notmuch-hello-query-counts (query-list &rest options) - "Compute list of counts of matched messages from QUERY-LIST. - -QUERY-LIST must be a list of saved-searches. Ideally each of -these is a plist but other options are available for backwards -compatibility: see `notmuch-saved-searches' for details. - -The result is a list of plists each of which includes the -properties :name NAME, :query QUERY and :count COUNT, together -with any properties in the original saved-search. - -The values :show-empty-searches, :filter and :filter-count from -options will be handled as specified for -`notmuch-hello-insert-searches'. :disable-includes can be used to -turn off the default exclude processing in `notmuch-count(1)'" - (with-temp-buffer - (dolist (elem query-list nil) - (let ((count-query (or (notmuch-saved-search-get elem :count-query) - (notmuch-saved-search-get elem :query)))) - (insert - (replace-regexp-in-string - "\n" " " - (notmuch-hello-filtered-query count-query - (or (plist-get options :filter-count) - (plist-get options :filter)))) - "\n"))) - (unless (= (notmuch--call-process-region (point-min) (point-max) notmuch-command - t t nil "count" - (if (plist-get options :disable-excludes) - "--exclude=false" - "--exclude=true") - "--batch") 0) - (notmuch-logged-error - "notmuch count --batch failed" - "Please check that the notmuch CLI is new enough to support `count ---batch'. In general we recommend running matching versions of -the CLI and emacs interface.")) - (goto-char (point-min)) - (cl-mapcan - (lambda (elem) - (let* ((elem-plist (notmuch-hello-saved-search-to-plist elem)) - (search-query (plist-get elem-plist :query)) - (filtered-query (notmuch-hello-filtered-query - search-query (plist-get options :filter))) - (message-count (prog1 (read (current-buffer)) - (forward-line 1)))) - (when (and filtered-query (or (plist-get options :show-empty-searches) - (> message-count 0))) - (setq elem-plist (plist-put elem-plist :query filtered-query)) - (list (plist-put elem-plist :count message-count))))) - query-list))) - -(defun notmuch-hello-nice-number (n) - (let (result) - (while (> n 0) - (push (% n 1000) result) - (setq n (/ n 1000))) - (setq result (or result '(0))) - (apply #'concat - (number-to-string (car result)) - (mapcar (lambda (elem) - (format "%s%03d" notmuch-hello-thousands-separator elem)) - (cdr result))))) - -(defun notmuch-hello-insert-buttons (searches) - "Insert buttons for SEARCHES. - -SEARCHES must be a list of plists each of which should contain at -least the properties :name NAME :query QUERY and :count COUNT, -where QUERY is the query to start when the button for the -corresponding entry is activated, and COUNT should be the number -of messages matching the query. Such a plist can be computed -with `notmuch-hello-query-counts'." - (let* ((widest (notmuch-hello-longest-label searches)) - (tags-and-width (notmuch-hello-tags-per-line widest)) - (tags-per-line (car tags-and-width)) - (column-width (cdr tags-and-width)) - (column-indent 0) - (count 0) - (reordered-list (notmuch-hello-reflect searches tags-per-line)) - ;; Hack the display of the buttons used. - (widget-push-button-prefix "") - (widget-push-button-suffix "")) - ;; dme: It feels as though there should be a better way to - ;; implement this loop than using an incrementing counter. - (mapc (lambda (elem) - ;; (not elem) indicates an empty slot in the matrix. - (when elem - (when (> column-indent 0) - (widget-insert (make-string column-indent ? ))) - (let* ((name (plist-get elem :name)) - (query (plist-get elem :query)) - (oldest-first (cl-case (plist-get elem :sort-order) - (newest-first nil) - (oldest-first t) - (otherwise notmuch-search-oldest-first))) - (search-type (plist-get elem :search-type)) - (msg-count (plist-get elem :count))) - (widget-insert (format "%8s " - (notmuch-hello-nice-number msg-count))) - (widget-create 'push-button - :notify #'notmuch-hello-widget-search - :notmuch-search-terms query - :notmuch-search-oldest-first oldest-first - :notmuch-search-type search-type - name) - (setq column-indent - (1+ (max 0 (- column-width (length name))))))) - (cl-incf count) - (when (eq (% count tags-per-line) 0) - (setq column-indent 0) - (widget-insert "\n"))) - reordered-list) - ;; If the last line was not full (and hence did not include a - ;; carriage return), insert one now. - (unless (eq (% count tags-per-line) 0) - (widget-insert "\n")))) - -;;; Mode - -(defun notmuch-hello-update () - "Update the notmuch-hello buffer." - ;; Lazy - rebuild everything. - (interactive) - (notmuch-hello t)) - -(defun notmuch-hello-window-configuration-change () - "Hook function to update the hello buffer when it is switched to." - (let ((hello-buf (get-buffer "*notmuch-hello*")) - (do-refresh nil)) - ;; Consider all windows in the currently selected frame, since - ;; that's where the configuration change happened. This also - ;; refreshes our snapshot of all windows, so we have to do this - ;; even if we know we won't refresh (e.g., hello-buf is null). - (dolist (window (window-list)) - (let ((last-buf (window-parameter window 'notmuch-hello-last-buffer)) - (cur-buf (window-buffer window))) - (unless (eq last-buf cur-buf) - ;; This window changed or is new. Update recorded buffer - ;; for next time. - (set-window-parameter window 'notmuch-hello-last-buffer cur-buf) - (when (and (eq cur-buf hello-buf) last-buf) - ;; The user just switched to hello in this window (hello - ;; is currently visible, was not visible on the last - ;; configuration change, and this is not a new window) - (setq do-refresh t))))) - (when (and do-refresh notmuch-hello-auto-refresh) - ;; Refresh hello as soon as we get back to redisplay. On Emacs - ;; 24, we can't do it right here because something in this - ;; hook's call stack overrides hello's point placement. - ;; FIXME And on Emacs releases that we still support? - (run-at-time nil nil #'notmuch-hello t)) - (unless hello-buf - ;; Clean up hook - (remove-hook 'window-configuration-change-hook - #'notmuch-hello-window-configuration-change)))) - -(defvar notmuch-hello-mode-map - ;; Inherit both widget-keymap and notmuch-common-keymap. We have - ;; to use make-sparse-keymap to force this to be a new keymap (so - ;; that when we modify map it does not modify widget-keymap). - (let ((map (make-composed-keymap (list (make-sparse-keymap) widget-keymap)))) - (set-keymap-parent map notmuch-common-keymap) - ;; Currently notmuch-hello-mode supports free text entry, but not - ;; tagging operations, so provide standard undo. - (define-key map [remap notmuch-tag-undo] #'undo) - map) - "Keymap for \"notmuch hello\" buffers.") - -(define-derived-mode notmuch-hello-mode fundamental-mode "notmuch-hello" - "Major mode for convenient notmuch navigation. This is your entry -portal into notmuch. - -Saved searches are \"bookmarks\" for arbitrary queries. Hit RET -or click on a saved search to view matching threads. Edit saved -searches with the `edit' button. Type `\\[notmuch-jump-search]' -in any Notmuch screen for quick access to saved searches that -have shortcut keys. - -Type new searches in the search box and hit RET to view matching -threads. Hit RET in a recent search box to re-submit a previous -search. Edit it first if you like. Save a recent search to saved -searches with the `save' button. - -Hit `\\[notmuch-search]' or `\\[notmuch-tree]' in any Notmuch -screen to search for messages and view matching threads or -messages, respectively. Recent searches are available in the -minibuffer history. - -Expand the all tags view with the `show' button (and collapse -again with the `hide' button). Hit RET or click on a tag name to -view matching threads. - -Hit `\\[notmuch-refresh-this-buffer]' to refresh the screen and -`\\[notmuch-bury-or-kill-this-buffer]' to quit. - -The screen may be customized via `\\[customize]'. - -Complete list of currently available key bindings: - -\\{notmuch-hello-mode-map}" - (setq notmuch-buffer-refresh-function #'notmuch-hello-update)) - -;;; Inserters - -(defun notmuch-hello-generate-tag-alist (&optional hide-tags) - "Return an alist from tags to queries to display in the all-tags section." - (cl-mapcan (lambda (tag) - (and (not (member tag hide-tags)) - (list (cons tag - (concat "tag:" - (notmuch-escape-boolean-term tag)))))) - (notmuch--process-lines notmuch-command "search" "--output=tags" "*"))) - -(defun notmuch-hello-insert-header () - "Insert the default notmuch-hello header." - (when notmuch-show-logo - (let ((image notmuch-hello-logo)) - ;; The notmuch logo uses transparency. That can display poorly - ;; when inserting the image into an emacs buffer (black logo on - ;; a black background), so force the background colour of the - ;; image. We use a face to represent the colour so that - ;; `defface' can be used to declare the different possible - ;; colours, which depend on whether the frame has a light or - ;; dark background. - (setq image (cons 'image - (append (cdr image) - (list :background - (face-background - 'notmuch-hello-logo-background))))) - (insert-image image)) - (widget-insert " ")) - - (widget-insert "Welcome to ") - ;; Hack the display of the links used. - (let ((widget-link-prefix "") - (widget-link-suffix "")) - (widget-create 'link - :notify (lambda (&rest _ignore) - (browse-url notmuch-hello-url)) - :help-echo "Visit the notmuch website." - "notmuch") - (widget-insert ". ") - (widget-insert "You have ") - (widget-create 'link - :notify (lambda (&rest _ignore) - (notmuch-hello-update)) - :help-echo "Refresh" - (notmuch-hello-nice-number - (string-to-number - (car (notmuch--process-lines notmuch-command "count" "--exclude=false"))))) - (widget-insert " messages.\n"))) - -(defun notmuch-hello-insert-saved-searches () - "Insert the saved-searches section." - (let ((searches (notmuch-hello-query-counts - (if notmuch-saved-search-sort-function - (funcall notmuch-saved-search-sort-function - notmuch-saved-searches) - notmuch-saved-searches) - :show-empty-searches notmuch-show-empty-saved-searches))) - (when searches - (widget-insert "Saved searches: ") - (widget-create 'push-button - :notify (lambda (&rest _ignore) - (customize-variable 'notmuch-saved-searches)) - "edit") - (widget-insert "\n\n") - (let ((start (point))) - (notmuch-hello-insert-buttons searches) - (indent-rigidly start (point) notmuch-hello-indent))))) - -(defun notmuch-hello-insert-search () - "Insert a search widget." - (widget-insert "Search: ") - (widget-create 'editable-field - ;; Leave some space at the start and end of the - ;; search boxes. - :size (max 8 (- (window-width) notmuch-hello-indent - (length "Search: "))) - :action #'notmuch-hello-search) - ;; Add an invisible dot to make `widget-end-of-line' ignore - ;; trailing spaces in the search widget field. A dot is used - ;; instead of a space to make `show-trailing-whitespace' - ;; happy, i.e. avoid it marking the whole line as trailing - ;; spaces. - (widget-insert (propertize "." 'invisible t)) - (widget-insert "\n")) - -(defun notmuch-hello-insert-recent-searches () - "Insert recent searches." - (when notmuch-search-history - (widget-insert "Recent searches: ") - (widget-create - 'push-button - :notify (lambda (&rest _ignore) - (when (y-or-n-p "Are you sure you want to clear the searches? ") - (setq notmuch-search-history nil) - (notmuch-hello-update))) - "clear") - (widget-insert "\n\n") - (let ((width (notmuch-search-item-field-width))) - (dolist (search (seq-take notmuch-search-history - notmuch-hello-recent-searches-max)) - (widget-create 'notmuch-search-item :value search :size width))))) - -(defun notmuch-hello-insert-searches (title query-list &rest options) - "Insert a section with TITLE showing a list of buttons made from -QUERY-LIST. - -QUERY-LIST should ideally be a plist but for backwards -compatibility other forms are also accepted (see -`notmuch-saved-searches' for details). The plist should -contain keys :name and :query; if :count-query is also present -then it specifies an alternate query to be used to generate the -count for the associated search. - -Supports the following entries in OPTIONS as a plist: -:initially-hidden - if non-nil, section will be hidden on startup -:show-empty-searches - show buttons with no matching messages -:hide-if-empty - hide if no buttons would be shown - (only makes sense without :show-empty-searches) -:filter - This can be a function that takes the search query as - its argument and returns a filter to be used in conjunction - with the query for that search or nil to hide the - element. This can also be a string that is used as a combined - with each query using \"and\". -:filter-count - Separate filter to generate the count displayed - each search. Accepts the same values as :filter. If :filter - and :filter-count are specified, this will be used instead of - :filter, not in conjunction with it." - - (widget-insert title ": ") - (when (and notmuch-hello-first-run (plist-get options :initially-hidden)) - (add-to-list 'notmuch-hello-hidden-sections title)) - (let ((is-hidden (member title notmuch-hello-hidden-sections)) - (start (point))) - (if is-hidden - (widget-create 'push-button - :notify (lambda (&rest _ignore) - (setq notmuch-hello-hidden-sections - (delete title notmuch-hello-hidden-sections)) - (notmuch-hello-update)) - "show") - (widget-create 'push-button - :notify (lambda (&rest _ignore) - (add-to-list 'notmuch-hello-hidden-sections - title) - (notmuch-hello-update)) - "hide")) - (widget-insert "\n") - (unless is-hidden - (let ((searches (apply 'notmuch-hello-query-counts query-list options))) - (when (or (not (plist-get options :hide-if-empty)) - searches) - (widget-insert "\n") - (notmuch-hello-insert-buttons searches) - (indent-rigidly start (point) notmuch-hello-indent)))))) - -(defun notmuch-hello-insert-tags-section (&optional title &rest options) - "Insert a section displaying all tags with message counts. - -TITLE defaults to \"All tags\". -Allowed options are those accepted by `notmuch-hello-insert-searches' and the -following: - -:hide-tags - List of tags that should be excluded." - (apply 'notmuch-hello-insert-searches - (or title "All tags") - (notmuch-hello-generate-tag-alist (plist-get options :hide-tags)) - options)) - -(defun notmuch-hello-insert-inbox () - "Show an entry for each saved search and inboxed messages for each tag." - (notmuch-hello-insert-searches "What's in your inbox" - (append - notmuch-saved-searches - (notmuch-hello-generate-tag-alist)) - :filter "tag:inbox")) - -(defun notmuch-hello-insert-alltags () - "Insert a section displaying all tags and associated message counts." - (notmuch-hello-insert-tags-section - nil - :initially-hidden (not notmuch-show-all-tags-list) - :hide-tags notmuch-hello-hide-tags - :filter notmuch-hello-tag-list-make-query - :disable-excludes t)) - -(defun notmuch-hello-insert-footer () - "Insert the notmuch-hello footer." - (let ((start (point))) - (widget-insert "Hit `?' for context-sensitive help in any Notmuch screen.\n") - (widget-insert "Customize ") - (widget-create 'link - :notify (lambda (&rest _ignore) - (customize-group 'notmuch)) - :button-prefix "" :button-suffix "" - "Notmuch") - (widget-insert " or ") - (widget-create 'link - :notify (lambda (&rest _ignore) - (customize-variable 'notmuch-hello-sections)) - :button-prefix "" :button-suffix "" - "this page.") - (let ((fill-column (- (window-width) notmuch-hello-indent))) - (center-region start (point))))) - -;;; Hello! - -;;;###autoload -(defun notmuch-hello (&optional no-display) - "Run notmuch and display saved searches, known tags, etc." - (interactive) - (notmuch-assert-cli-sane) - ;; This may cause a window configuration change, so if the - ;; auto-refresh hook is already installed, avoid recursive refresh. - (let ((notmuch-hello-auto-refresh nil)) - (if no-display - (set-buffer "*notmuch-hello*") - (pop-to-buffer-same-window "*notmuch-hello*"))) - ;; Install auto-refresh hook - (when notmuch-hello-auto-refresh - (add-hook 'window-configuration-change-hook - #'notmuch-hello-window-configuration-change)) - (let ((target-line (line-number-at-pos)) - (target-column (current-column)) - (inhibit-read-only t)) - ;; Delete all editable widget fields. Editable widget fields are - ;; tracked in a buffer local variable `widget-field-list' (and - ;; others). If we do `erase-buffer' without properly deleting the - ;; widgets, some widget-related functions are confused later. - (mapc 'widget-delete widget-field-list) - (erase-buffer) - (unless (eq major-mode 'notmuch-hello-mode) - (notmuch-hello-mode)) - (let ((all (overlay-lists))) - ;; Delete all the overlays. - (mapc 'delete-overlay (car all)) - (mapc 'delete-overlay (cdr all))) - (mapc - (lambda (section) - (let ((point-before (point))) - (if (functionp section) - (funcall section) - (apply (car section) (cdr section))) - ;; don't insert a newline when the previous section didn't - ;; show anything. - (unless (eq (point) point-before) - (widget-insert "\n")))) - notmuch-hello-sections) - (widget-setup) - ;; Move point back to where it was before refresh. Use line and - ;; column instead of point directly to be insensitive to additions - ;; and removals of text within earlier lines. - (goto-char (point-min)) - (forward-line (1- target-line)) - (move-to-column target-column)) - (run-hooks 'notmuch-hello-refresh-hook) - (setq notmuch-hello-first-run nil)) - -;;; _ - -(provide 'notmuch-hello) - -;;; notmuch-hello.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-hello.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-hello.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-jump.el b/emacs/elpa/notmuch-20231006.2337/notmuch-jump.el @@ -1,210 +0,0 @@ -;;; notmuch-jump.el --- User-friendly shortcut keys -*- lexical-binding: t -*- -;; -;; Copyright © Austin Clements -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: Austin Clements <aclements@csail.mit.edu> -;; David Edmondson <dme@dme.org> - -;;; Code: - -(require 'notmuch-lib) -(require 'notmuch-hello) - -(declare-function notmuch-search "notmuch") -(declare-function notmuch-tree "notmuch-tree") -(declare-function notmuch-unthreaded "notmuch-tree") - -;;;###autoload -(defun notmuch-jump-search () - "Jump to a saved search by shortcut key. - -This prompts for and performs a saved search using the shortcut -keys configured in the :key property of `notmuch-saved-searches'. -Typically these shortcuts are a single key long, so this is a -fast way to jump to a saved search from anywhere in Notmuch." - (interactive) - ;; Build the action map - (let (action-map) - (dolist (saved-search notmuch-saved-searches) - (let* ((saved-search (notmuch-hello-saved-search-to-plist saved-search)) - (key (plist-get saved-search :key))) - (when key - (let ((name (plist-get saved-search :name)) - (query (plist-get saved-search :query)) - (oldest-first - (cl-case (plist-get saved-search :sort-order) - (newest-first nil) - (oldest-first t) - (otherwise (default-value 'notmuch-search-oldest-first))))) - (push (list key name - (cond - ((eq (plist-get saved-search :search-type) 'tree) - (lambda () (notmuch-tree query))) - ((eq (plist-get saved-search :search-type) 'unthreaded) - (lambda () (notmuch-unthreaded query))) - (t - (lambda () (notmuch-search query oldest-first))))) - action-map))))) - (setq action-map (nreverse action-map)) - (if action-map - (notmuch-jump action-map "Search: ") - (error "To use notmuch-jump, %s" - "please customize shortcut keys in notmuch-saved-searches.")))) - -(defface notmuch-jump-key - '((t :inherit minibuffer-prompt)) - "Default face used for keys in `notmuch-jump' and related." - :group 'notmuch-faces) - -(defvar notmuch-jump--action nil) - -;;;###autoload -(defun notmuch-jump (action-map prompt) - "Interactively prompt for one of the keys in ACTION-MAP. - -Displays a summary of all bindings in ACTION-MAP in the -minibuffer, reads a key from the minibuffer, and performs the -corresponding action. The prompt can be canceled with C-g or -RET. PROMPT must be a string to use for the prompt. PROMPT -should include a space at the end. - -ACTION-MAP must be a list of triples of the form - (KEY LABEL ACTION) -where KEY is a key binding, LABEL is a string label to display in -the buffer, and ACTION is a nullary function to call. LABEL may -be null, in which case the action will still be bound, but will -not appear in the pop-up buffer." - (let* ((items (notmuch-jump--format-actions action-map)) - ;; Format the table of bindings and the full prompt - (table - (with-temp-buffer - (notmuch-jump--insert-items (window-body-width) items) - (buffer-string))) - (full-prompt - (concat table "\n\n" - (propertize prompt 'face 'minibuffer-prompt))) - ;; By default, the minibuffer applies the minibuffer face to - ;; the entire prompt. However, we want to clearly - ;; distinguish bindings (which we put in the prompt face - ;; ourselves) from their labels, so disable the minibuffer's - ;; own re-face-ing. - (minibuffer-prompt-properties - (notmuch-plist-delete - (copy-sequence minibuffer-prompt-properties) - 'face)) - ;; Build the keymap with our bindings - (minibuffer-map (notmuch-jump--make-keymap action-map prompt)) - ;; The bindings save the the action in notmuch-jump--action - (notmuch-jump--action nil)) - ;; Read the action - (read-from-minibuffer full-prompt nil minibuffer-map) - ;; If we got an action, do it - (when notmuch-jump--action - (funcall notmuch-jump--action)))) - -(defun notmuch-jump--format-actions (action-map) - "Format the actions in ACTION-MAP. - -Returns a list of strings, one for each item with a label in -ACTION-MAP. These strings can be inserted into a tabular -buffer." - ;; Compute the maximum key description width - (let ((key-width 1)) - (pcase-dolist (`(,key ,_desc) action-map) - (setq key-width - (max key-width - (string-width (format-kbd-macro key))))) - ;; Format each action - (mapcar (pcase-lambda (`(,key ,desc)) - (setq key (format-kbd-macro key)) - (concat (propertize key 'face 'notmuch-jump-key) - (make-string (- key-width (length key)) ? ) - " " desc)) - action-map))) - -(defun notmuch-jump--insert-items (width items) - "Make a table of ITEMS up to WIDTH wide in the current buffer." - (let* ((nitems (length items)) - (col-width (+ 3 (apply #'max (mapcar #'string-width items)))) - (ncols (if (> (* col-width nitems) width) - (max 1 (/ width col-width)) - ;; Items fit on one line. Space them out - (setq col-width (/ width nitems)) - (length items)))) - (while items - (dotimes (col ncols) - (when items - (let ((item (pop items))) - (insert item) - (when (and items (< col (- ncols 1))) - (insert (make-string (- col-width (string-width item)) ? )))))) - (when items - (insert "\n"))))) - -(defvar notmuch-jump-minibuffer-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map minibuffer-local-map) - ;; Make this like a special-mode keymap, with no self-insert-command - (suppress-keymap map) - (define-key map (kbd "DEL") 'exit-minibuffer) - map) - "Base keymap for notmuch-jump's minibuffer keymap.") - -(defun notmuch-jump--make-keymap (action-map prompt) - "Translate ACTION-MAP into a minibuffer keymap." - (let ((map (make-sparse-keymap))) - (set-keymap-parent map notmuch-jump-minibuffer-map) - (pcase-dolist (`(,key ,_name ,fn) action-map) - (when (= (length key) 1) - (define-key map key - (lambda () - (interactive) - (setq notmuch-jump--action fn) - (exit-minibuffer))))) - ;; By doing this in two passes (and checking if we already have a - ;; binding) we avoid problems if the user specifies a binding which - ;; is a prefix of another binding. - (pcase-dolist (`(,key ,_name ,_fn) action-map) - (when (> (length key) 1) - (let* ((key (elt key 0)) - (keystr (string key)) - (new-prompt (concat prompt (format-kbd-macro keystr) " ")) - (action-submap nil)) - (unless (lookup-key map keystr) - (pcase-dolist (`(,k ,n ,f) action-map) - (when (= key (elt k 0)) - (push (list (substring k 1) n f) action-submap))) - ;; We deal with backspace specially - (push (list (kbd "DEL") - "Backup" - (apply-partially #'notmuch-jump action-map prompt)) - action-submap) - (setq action-submap (nreverse action-submap)) - (define-key map keystr - (lambda () - (interactive) - (setq notmuch-jump--action - (apply-partially #'notmuch-jump - action-submap - new-prompt)) - (exit-minibuffer))))))) - map)) - -(provide 'notmuch-jump) - -;;; notmuch-jump.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-jump.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-jump.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-lib.el b/emacs/elpa/notmuch-20231006.2337/notmuch-lib.el @@ -1,1075 +0,0 @@ -;;; notmuch-lib.el --- common variables, functions and function declarations -*- lexical-binding: t -*- -;; -;; Copyright © Carl Worth -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: Carl Worth <cworth@cworth.org> - -;;; Code: - -(require 'cl-lib) -(require 'pcase) -(require 'subr-x) - -(require 'mm-util) -(require 'mm-view) -(require 'mm-decode) - -(require 'notmuch-compat) - -(unless (require 'notmuch-version nil t) - (defconst notmuch-emacs-version "unknown" - "Placeholder variable when notmuch-version.el[c] is not available.")) - -;;; Groups - -(defgroup notmuch nil - "Notmuch mail reader for Emacs." - :group 'mail) - -(defgroup notmuch-hello nil - "Overview of saved searches, tags, etc." - :group 'notmuch) - -(defgroup notmuch-search nil - "Searching and sorting mail." - :group 'notmuch) - -(defgroup notmuch-show nil - "Showing messages and threads." - :group 'notmuch) - -(defgroup notmuch-send nil - "Sending messages from Notmuch." - :group 'notmuch - :group 'message) - -(defgroup notmuch-tag nil - "Tags and tagging in Notmuch." - :group 'notmuch) - -(defgroup notmuch-crypto nil - "Processing and display of cryptographic MIME parts." - :group 'notmuch) - -(defgroup notmuch-hooks nil - "Running custom code on well-defined occasions." - :group 'notmuch) - -(defgroup notmuch-external nil - "Running external commands from within Notmuch." - :group 'notmuch) - -(defgroup notmuch-address nil - "Address completion." - :group 'notmuch) - -(defgroup notmuch-faces nil - "Graphical attributes for displaying text" - :group 'notmuch) - -;;; Options - -(defcustom notmuch-command "notmuch" - "Name of the notmuch binary. - -This can be a relative or absolute path to the notmuch binary. -If this is a relative path, it will be searched for in all of the -directories given in `exec-path' (which is, by default, based on -$PATH)." - :type 'string - :group 'notmuch-external) - -(defcustom notmuch-search-oldest-first t - "Show the oldest mail first when searching. - -This variable defines the default sort order for displaying -search results. Note that any filtered searches created by -`notmuch-search-filter' retain the search order of the parent -search." - :type 'boolean - :group 'notmuch-search) -(make-variable-buffer-local 'notmuch-search-oldest-first) - -(defcustom notmuch-poll-script nil - "[Deprecated] Command to run to incorporate new mail into the notmuch database. - -This option has been deprecated in favor of \"notmuch new\" -hooks (see man notmuch-hooks). To change the path to the notmuch -binary, customize `notmuch-command'. - -This variable controls the action invoked by -`notmuch-poll-and-refresh-this-buffer' (bound by default to 'G') -to incorporate new mail into the notmuch database. - -If set to nil (the default), new mail is processed by invoking -\"notmuch new\". Otherwise, this should be set to a string that -gives the name of an external script that processes new mail. If -set to the empty string, no command will be run. - -The external script could do any of the following depending on -the user's needs: - -1. Invoke a program to transfer mail to the local mail store -2. Invoke \"notmuch new\" to incorporate the new mail -3. Invoke one or more \"notmuch tag\" commands to classify the mail" - :type '(choice (const :tag "notmuch new" nil) - (const :tag "Disabled" "") - (string :tag "Custom script")) - :group 'notmuch-external) - -(defcustom notmuch-archive-tags '("-inbox") - "List of tag changes to apply to a message or a thread when it is archived. - -Tags starting with \"+\" (or not starting with either \"+\" or -\"-\") in the list will be added, and tags starting with \"-\" -will be removed from the message or thread being archived. - -For example, if you wanted to remove an \"inbox\" tag and add an -\"archived\" tag, you would set: - (\"-inbox\" \"+archived\")" - :type '(repeat string) - :group 'notmuch-search - :group 'notmuch-show) - -;;; Variables - -(defvar notmuch-search-history nil - "Variable to store notmuch searches history.") - -(defvar notmuch-common-keymap - (let ((map (make-sparse-keymap))) - (define-key map "?" 'notmuch-help) - (define-key map "v" 'notmuch-version) - (define-key map "q" 'notmuch-bury-or-kill-this-buffer) - (define-key map "s" 'notmuch-search) - (define-key map "t" 'notmuch-search-by-tag) - (define-key map "z" 'notmuch-tree) - (define-key map "u" 'notmuch-unthreaded) - (define-key map "m" 'notmuch-mua-new-mail) - (define-key map "g" 'notmuch-refresh-this-buffer) - (define-key map "=" 'notmuch-refresh-this-buffer) - (define-key map (kbd "M-=") 'notmuch-refresh-all-buffers) - (define-key map "G" 'notmuch-poll-and-refresh-this-buffer) - (define-key map "j" 'notmuch-jump-search) - (define-key map [remap undo] 'notmuch-tag-undo) - map) - "Keymap shared by all notmuch modes.") - -;; By default clicking on a button does not select the window -;; containing the button (as opposed to clicking on a widget which -;; does). This means that the button action is then executed in the -;; current selected window which can cause problems if the button -;; changes the buffer (e.g., id: links) or moves point. -;; -;; This provides a button type which overrides mouse-action so that -;; the button's window is selected before the action is run. Other -;; notmuch buttons can get the same behaviour by inheriting from this -;; button type. -(define-button-type 'notmuch-button-type - 'mouse-action (lambda (button) - (select-window (posn-window (event-start last-input-event))) - (button-activate button))) - -;;; CLI Utilities - -(defun notmuch-command-to-string (&rest args) - "Synchronously invoke \"notmuch\" with the given list of arguments. - -If notmuch exits with a non-zero status, output from the process -will appear in a buffer named \"*Notmuch errors*\" and an error -will be signaled. - -Otherwise the output will be returned." - (with-temp-buffer - (let ((status (apply #'notmuch--call-process notmuch-command nil t nil args)) - (output (buffer-string))) - (notmuch-check-exit-status status (cons notmuch-command args) output) - output))) - -(defvar notmuch--cli-sane-p nil - "Cache whether the CLI seems to be configured sanely.") - -(defun notmuch-cli-sane-p () - "Return t if the cli seems to be configured sanely." - (unless notmuch--cli-sane-p - (let ((status (notmuch--call-process notmuch-command nil nil nil - "config" "get" "user.primary_email"))) - (setq notmuch--cli-sane-p (= status 0)))) - notmuch--cli-sane-p) - -(defun notmuch-assert-cli-sane () - (unless (notmuch-cli-sane-p) - (notmuch-logged-error - "notmuch cli seems misconfigured or unconfigured." - "Perhaps you haven't run \"notmuch setup\" yet? Try running this -on the command line, and then retry your notmuch command"))) - -(defun notmuch-cli-version () - "Return a string with the notmuch cli command version number." - (let ((long-string - ;; Trim off the trailing newline. - (substring (notmuch-command-to-string "--version") 0 -1))) - (if (string-match "^notmuch\\( version\\)? \\(.*\\)$" - long-string) - (match-string 2 long-string) - "unknown"))) - -(defvar notmuch-emacs-version) - -(defun notmuch-version () - "Display the notmuch version. -The versions of the Emacs package and the `notmuch' executable -should match, but if and only if they don't, then this command -displays both values separately." - (interactive) - (let ((cli-version (notmuch-cli-version))) - (message "notmuch version %s" - (if (string= notmuch-emacs-version cli-version) - cli-version - (concat cli-version - " (emacs mua version " notmuch-emacs-version ")"))))) - -;;; Notmuch Configuration - -(defun notmuch-config-get (item) - "Return a value from the notmuch configuration." - (let* ((val (notmuch-command-to-string "config" "get" item)) - (len (length val))) - ;; Trim off the trailing newline (if the value is empty or not - ;; configured, there will be no newline). - (if (and (> len 0) - (= (aref val (- len 1)) ?\n)) - (substring val 0 -1) - val))) - -(defun notmuch-database-path () - "Return the database.path value from the notmuch configuration." - (notmuch-config-get "database.path")) - -(defun notmuch-user-name () - "Return the user.name value from the notmuch configuration." - (notmuch-config-get "user.name")) - -(defun notmuch-user-primary-email () - "Return the user.primary_email value from the notmuch configuration." - (notmuch-config-get "user.primary_email")) - -(defun notmuch-user-other-email () - "Return the user.other_email value (as a list) from the notmuch configuration." - (split-string (notmuch-config-get "user.other_email") "\n" t)) - -(defun notmuch-user-emails () - (cons (notmuch-user-primary-email) (notmuch-user-other-email))) - -;;; Commands - -(defun notmuch-poll () - "Run \"notmuch new\" or an external script to import mail. - -Invokes `notmuch-poll-script', \"notmuch new\", or does nothing -depending on the value of `notmuch-poll-script'." - (interactive) - (message "Polling mail...") - (if (stringp notmuch-poll-script) - (unless (string-empty-p notmuch-poll-script) - (unless (equal (notmuch--call-process notmuch-poll-script nil nil) 0) - (error "Notmuch: poll script `%s' failed!" notmuch-poll-script))) - (notmuch-call-notmuch-process "new")) - (message "Polling mail...done")) - -(defun notmuch-bury-or-kill-this-buffer () - "Undisplay the current buffer. - -Bury the current buffer, unless there is only one window showing -it, in which case it is killed." - (interactive) - (if (> (length (get-buffer-window-list nil nil t)) 1) - (bury-buffer) - (kill-buffer))) - -;;; Describe Key Bindings - -(defun notmuch-prefix-key-description (key) - "Given a prefix key code, return a human-readable string representation. - -This is basically just `format-kbd-macro' but we also convert ESC to M-." - (let* ((key-vector (if (vectorp key) key (vector key))) - (desc (format-kbd-macro key-vector))) - (if (string= desc "ESC") - "M-" - (concat desc " ")))) - -(defun notmuch-describe-key (actual-key binding prefix ua-keys tail) - "Prepend cons cells describing prefix-arg ACTUAL-KEY and ACTUAL-KEY to TAIL. - -It does not prepend if ACTUAL-KEY is already listed in TAIL." - (let ((key-string (concat prefix (key-description actual-key)))) - ;; We don't include documentation if the key-binding is - ;; over-ridden. Note, over-riding a binding automatically hides the - ;; prefixed version too. - (unless (assoc key-string tail) - (when (and ua-keys (symbolp binding) - (get binding 'notmuch-prefix-doc)) - ;; Documentation for prefixed command - (let ((ua-desc (key-description ua-keys))) - (push (cons (concat ua-desc " " prefix (format-kbd-macro actual-key)) - (get binding 'notmuch-prefix-doc)) - tail))) - ;; Documentation for command - (push (cons key-string - (or (and (symbolp binding) - (get binding 'notmuch-doc)) - (and (functionp binding) - (let ((doc (documentation binding))) - (and doc - (string-match "\\`.+" doc) - (match-string 0 doc)))))) - tail))) - tail) - -(defun notmuch-describe-remaps (remap-keymap ua-keys base-keymap prefix tail) - ;; Remappings are represented as a binding whose first "event" is - ;; 'remap. Hence, if the keymap has any remappings, it will have a - ;; binding whose "key" is 'remap, and whose "binding" is itself a - ;; keymap that maps not from keys to commands, but from old (remapped) - ;; functions to the commands to use in their stead. - (map-keymap (lambda (command binding) - (mapc (lambda (actual-key) - (setq tail - (notmuch-describe-key actual-key binding - prefix ua-keys tail))) - (where-is-internal command base-keymap))) - remap-keymap) - tail) - -(defun notmuch-describe-keymap (keymap ua-keys base-keymap &optional prefix tail) - "Return a list of cons cells, each describing one binding in KEYMAP. - -Each cons cell consists of a string giving a human-readable -description of the key, and a one-line description of the bound -function. See `notmuch-help' for an overview of how this -documentation is extracted. - -UA-KEYS should be a key sequence bound to `universal-argument'. -It will be used to describe bindings of commands that support a -prefix argument. PREFIX and TAIL are used internally." - (map-keymap - (lambda (key binding) - (cond ((mouse-event-p key) nil) - ((keymapp binding) - (setq tail - (if (eq key 'remap) - (notmuch-describe-remaps - binding ua-keys base-keymap prefix tail) - (notmuch-describe-keymap - binding ua-keys base-keymap - (notmuch-prefix-key-description key) - tail)))) - (binding - (setq tail - (notmuch-describe-key (vector key) - binding prefix ua-keys tail))))) - keymap) - tail) - -(defun notmuch-substitute-command-keys (doc) - "Like `substitute-command-keys' but with documentation, not function names." - (let ((beg 0)) - (while (string-match "\\\\{\\([^}[:space:]]*\\)}" doc beg) - (let ((desc - (save-match-data - (let* ((keymap-name (substring doc - (match-beginning 1) - (match-end 1))) - (keymap (symbol-value (intern keymap-name))) - (ua-keys (where-is-internal 'universal-argument keymap t)) - (desc-alist (notmuch-describe-keymap keymap ua-keys keymap)) - (desc-list (mapcar (lambda (arg) - (concat (car arg) "\t" (cdr arg))) - desc-alist))) - (mapconcat #'identity desc-list "\n"))))) - (setq doc (replace-match desc 1 1 doc))) - (setq beg (match-end 0))) - doc)) - -(defun notmuch-help () - "Display help for the current notmuch mode. - -This is similar to `describe-function' for the current major -mode, but bindings tables are shown with documentation strings -rather than command names. By default, this uses the first line -of each command's documentation string. A command can override -this by setting the \\='notmuch-doc property of its command symbol. -A command that supports a prefix argument can explicitly document -its prefixed behavior by setting the \\='notmuch-prefix-doc property -of its command symbol." - (interactive) - (let ((doc (substitute-command-keys - (notmuch-substitute-command-keys - (documentation major-mode t))))) - (with-current-buffer (generate-new-buffer "*notmuch-help*") - (insert doc) - (goto-char (point-min)) - (set-buffer-modified-p nil) - (view-buffer (current-buffer) 'kill-buffer-if-not-modified)))) - -(defun notmuch-subkeymap-help () - "Show help for a subkeymap." - (interactive) - (let* ((key (this-command-keys-vector)) - (prefix (make-vector (1- (length key)) nil)) - (i 0)) - (while (< i (length prefix)) - (aset prefix i (aref key i)) - (cl-incf i)) - (let* ((subkeymap (key-binding prefix)) - (ua-keys (where-is-internal 'universal-argument nil t)) - (prefix-string (notmuch-prefix-key-description prefix)) - (desc-alist (notmuch-describe-keymap - subkeymap ua-keys subkeymap prefix-string)) - (desc-list (mapcar (lambda (arg) (concat (car arg) "\t" (cdr arg))) - desc-alist)) - (desc (mapconcat #'identity desc-list "\n"))) - (with-help-window (help-buffer) - (with-current-buffer standard-output - (insert "\nPress 'q' to quit this window.\n\n") - (insert desc))) - (pop-to-buffer (help-buffer))))) - -;;; Refreshing Buffers - -(defvar-local notmuch-buffer-refresh-function nil - "Function to call to refresh the current buffer.") - -(defun notmuch-refresh-this-buffer () - "Refresh the current buffer." - (interactive) - (when notmuch-buffer-refresh-function - ;; Pass prefix argument, etc. - (call-interactively notmuch-buffer-refresh-function))) - -(defun notmuch-poll-and-refresh-this-buffer () - "Invoke `notmuch-poll' to import mail, then refresh the current buffer." - (interactive) - (notmuch-poll) - (notmuch-refresh-this-buffer)) - -(defun notmuch-refresh-all-buffers () - "Invoke `notmuch-refresh-this-buffer' on all notmuch major-mode buffers. - -The buffers are silently refreshed, i.e. they are not forced to -be displayed." - (interactive) - (dolist (buffer (buffer-list)) - (let ((buffer-mode (buffer-local-value 'major-mode buffer))) - (when (memq buffer-mode '(notmuch-show-mode - notmuch-tree-mode - notmuch-search-mode - notmuch-hello-mode)) - (with-current-buffer buffer - (notmuch-refresh-this-buffer)))))) - -;;; String Utilities - -(defun notmuch-prettify-subject (subject) - ;; This function is used by `notmuch-search-process-filter', - ;; which requires that we not disrupt its matching state. - (save-match-data - (if (and subject - (string-match "^[ \t]*$" subject)) - "[No Subject]" - subject))) - -(defun notmuch-sanitize (str) - "Sanitize control character in STR. - -This includes newlines, tabs, and other funny characters." - (replace-regexp-in-string "[[:cntrl:]\x7f\u2028\u2029]+" " " str)) - -(defun notmuch-escape-boolean-term (term) - "Escape a boolean term for use in a query. - -The caller is responsible for prepending the term prefix and a -colon. This performs minimal escaping in order to produce -user-friendly queries." - (save-match-data - (if (or (equal term "") - ;; To be pessimistic, only pass through terms composed - ;; entirely of ASCII printing characters other than ", (, - ;; and ). - (string-match "[^!#-'*-~]" term)) - ;; Requires escaping - (concat "\"" (replace-regexp-in-string "\"" "\"\"" term t t) "\"") - term))) - -(defun notmuch-id-to-query (id) - "Return a query that matches the message with id ID." - (concat "id:" (notmuch-escape-boolean-term id))) - -(defun notmuch-hex-encode (str) - "Hex-encode STR (e.g., as used by batch tagging). - -This replaces spaces, percents, and double quotes in STR with -%NN where NN is the hexadecimal value of the character." - (replace-regexp-in-string - "[ %\"]" (lambda (match) (format "%%%02x" (aref match 0))) str)) - -(defun notmuch-common-do-stash (text) - "Common function to stash text in kill ring, and display in minibuffer." - (if text - (progn - (kill-new text) - (message "Stashed: %s" text)) - ;; There is nothing to stash so stash an empty string so the user - ;; doesn't accidentally paste something else somewhere. - (kill-new "") - (message "Nothing to stash!"))) - -;;; Generic Utilities - -(defun notmuch-plist-delete (plist property) - (let (p) - (while plist - (unless (eq property (car plist)) - (setq p (plist-put p (car plist) (cadr plist)))) - (setq plist (cddr plist))) - p)) - -;;; MML Utilities - -(defun notmuch-match-content-type (t1 t2) - "Return t if t1 and t2 are matching content types. -Take wildcards into account." - (and (stringp t1) - (stringp t2) - (let ((st1 (split-string t1 "/")) - (st2 (split-string t2 "/"))) - (if (or (string= (cadr st1) "*") - (string= (cadr st2) "*")) - ;; Comparison of content types should be case insensitive. - (string= (downcase (car st1)) - (downcase (car st2))) - (string= (downcase t1) - (downcase t2)))))) - -(defcustom notmuch-multipart/alternative-discouraged - '(;; Avoid HTML parts. - "text/html" - ;; multipart/related usually contain a text/html part and some - ;; associated graphics. - "multipart/related") - "Which mime types to hide by default for multipart messages. - -Can either be a list of mime types (as strings) or a function -mapping a plist representing the current message to such a list. -See Info node `(notmuch-emacs) notmuch-show' for a sample function." - :group 'notmuch-show - :type '(radio (repeat :tag "MIME Types" string) - (function :tag "Function"))) - -(defun notmuch-multipart/alternative-determine-discouraged (msg) - "Return the discouraged alternatives for the specified message." - ;; If a function, return the result of calling it. - (if (functionp notmuch-multipart/alternative-discouraged) - (funcall notmuch-multipart/alternative-discouraged msg) - ;; Otherwise simply return the value of the variable, which is - ;; assumed to be a list of discouraged alternatives. This is the - ;; default behaviour. - notmuch-multipart/alternative-discouraged)) - -(defun notmuch-multipart/alternative-choose (msg types) - "Return a list of preferred types from the given list of types -for this message, if present." - ;; Based on `mm-preferred-alternative-precedence'. - (let ((discouraged (notmuch-multipart/alternative-determine-discouraged msg)) - (seq types)) - (dolist (pref (reverse discouraged)) - (dolist (elem (copy-sequence seq)) - (when (string-match pref elem) - (setq seq (nconc (delete elem seq) (list elem)))))) - seq)) - -(defun notmuch-parts-filter-by-type (parts type) - "Given a list of message parts, return a list containing the ones matching -the given type." - (cl-remove-if-not - (lambda (part) (notmuch-match-content-type (plist-get part :content-type) type)) - parts)) - -(defun notmuch--get-bodypart-raw (msg part process-crypto binaryp cache) - (let* ((plist-elem (if binaryp :content-binary :content)) - (data (or (plist-get part plist-elem) - (with-temp-buffer - ;; Emacs internally uses a UTF-8-like multibyte string - ;; representation by default (regardless of the coding - ;; system, which only affects how it goes from outside data - ;; to this internal representation). This *almost* never - ;; matters. Annoyingly, it does matter if we use this data - ;; in an image descriptor, since Emacs will use its internal - ;; data buffer directly and this multibyte representation - ;; corrupts binary image formats. Since the caller is - ;; asking for binary data, a unibyte string is a more - ;; appropriate representation anyway. - (when binaryp - (set-buffer-multibyte nil)) - (let ((args `("show" "--format=raw" - ,(format "--part=%s" (plist-get part :id)) - ,@(and process-crypto '("--decrypt=true")) - ,(notmuch-id-to-query (plist-get msg :id)))) - (coding-system-for-read - (if binaryp - 'no-conversion - (let ((coding-system - (mm-charset-to-coding-system - (plist-get part :content-charset)))) - ;; Sadly, - ;; `mm-charset-to-coding-system' seems - ;; to return things that are not - ;; considered acceptable values for - ;; `coding-system-for-read'. - (if (coding-system-p coding-system) - coding-system - ;; RFC 2047 says that the default - ;; charset is US-ASCII. RFC6657 - ;; complicates this somewhat. - 'us-ascii))))) - (apply #'notmuch--call-process - notmuch-command nil '(t nil) nil args) - (buffer-string)))))) - (when (and cache data) - (plist-put part plist-elem data)) - data)) - -(defun notmuch-get-bodypart-binary (msg part process-crypto &optional cache) - "Return the unprocessed content of PART in MSG as a unibyte string. - -This returns the \"raw\" content of the given part after content -transfer decoding, but with no further processing (see the -discussion of --format=raw in man notmuch-show). In particular, -this does no charset conversion. - -If CACHE is non-nil, the content of this part will be saved in -MSG (if it isn't already)." - (notmuch--get-bodypart-raw msg part process-crypto t cache)) - -(defun notmuch-get-bodypart-text (msg part process-crypto &optional cache) - "Return the text content of PART in MSG. - -This returns the content of the given part as a multibyte Lisp -string after performing content transfer decoding and any -necessary charset decoding. - -If CACHE is non-nil, the content of this part will be saved in -MSG (if it isn't already)." - (notmuch--get-bodypart-raw msg part process-crypto nil cache)) - -(defun notmuch-mm-display-part-inline (msg part content-type process-crypto) - "Use the mm-decode/mm-view functions to display a part in the -current buffer, if possible." - (let ((display-buffer (current-buffer))) - (with-temp-buffer - ;; In case we already have :content, use it and tell mm-* that - ;; it's already been charset-decoded by using the fake - ;; `gnus-decoded' charset. Otherwise, we'll fetch the binary - ;; part content and let mm-* decode it. - (let* ((have-content (plist-member part :content)) - (charset (if have-content - 'gnus-decoded - (plist-get part :content-charset))) - (handle (mm-make-handle (current-buffer) - `(,content-type (charset . ,charset))))) - ;; If the user wants the part inlined, insert the content and - ;; test whether we are able to inline it (which includes both - ;; capability and suitability tests). - (when (mm-inlined-p handle) - (if have-content - (insert (notmuch-get-bodypart-text msg part process-crypto)) - (insert (notmuch-get-bodypart-binary msg part process-crypto))) - (when (mm-inlinable-p handle) - (set-buffer display-buffer) - (mm-display-part handle) - (plist-put part :undisplayer (mm-handle-undisplayer handle)) - t)))))) - -;;; Generic Utilities - -;; Converts a plist of headers to an alist of headers. The input plist should -;; have symbols of the form :Header as keys, and the resulting alist will have -;; symbols of the form 'Header as keys. -(defun notmuch-headers-plist-to-alist (plist) - (cl-loop for (key value . rest) on plist by #'cddr - collect (cons (intern (substring (symbol-name key) 1)) value))) - -(defun notmuch-face-ensure-list-form (face) - "Return FACE in face list form. - -If FACE is already a face list, it will be returned as-is. If -FACE is a face name or face plist, it will be returned as a -single element face list." - (if (and (listp face) (not (keywordp (car face)))) - face - (list face))) - -(defun notmuch-apply-face (object face &optional below start end) - "Combine FACE into the \\='face text property of OBJECT between START and END. - -This function combines FACE with any existing faces between START -and END in OBJECT. Attributes specified by FACE take precedence -over existing attributes unless BELOW is non-nil. - -OBJECT may be a string, a buffer, or nil (which means the current -buffer). If object is a string, START and END are 0-based; -otherwise they are buffer positions (integers or markers). FACE -must be a face name (a symbol or string), a property list of face -attributes, or a list of these. If START and/or END are omitted, -they default to the beginning/end of OBJECT. For convenience -when applied to strings, this returns OBJECT." - ;; A face property can have three forms: a face name (a string or - ;; symbol), a property list, or a list of these two forms. In the - ;; list case, the faces will be combined, with the earlier faces - ;; taking precedent. Here we canonicalize everything to list form - ;; to make it easy to combine. - (let ((pos (cond (start start) - ((stringp object) 0) - (t 1))) - (end (cond (end end) - ((stringp object) (length object)) - (t (1+ (buffer-size object))))) - (face-list (notmuch-face-ensure-list-form face))) - (while (< pos end) - (let* ((cur (get-text-property pos 'face object)) - (cur-list (notmuch-face-ensure-list-form cur)) - (new (cond ((null cur-list) face) - (below (append cur-list face-list)) - (t (append face-list cur-list)))) - (next (next-single-property-change pos 'face object end))) - (put-text-property pos next 'face new object) - (setq pos next)))) - object) - -(defun notmuch-map-text-property (start end prop func &optional object) - "Transform text property PROP using FUNC. - -Applies FUNC to each distinct value of the text property PROP -between START and END of OBJECT, setting PROP to the value -returned by FUNC." - (while (< start end) - (let ((value (get-text-property start prop object)) - (next (next-single-property-change start prop object end))) - (put-text-property start next prop (funcall func value) object) - (setq start next)))) - -;;; Running Notmuch - -(defun notmuch-logged-error (msg &optional extra) - "Log MSG and EXTRA to *Notmuch errors* and signal MSG. - -This logs MSG and EXTRA to the *Notmuch errors* buffer and -signals MSG as an error. If EXTRA is non-nil, text referring the -user to the *Notmuch errors* buffer will be appended to the -signaled error. This function does not return." - (with-current-buffer (get-buffer-create "*Notmuch errors*") - (goto-char (point-max)) - (unless (bobp) - (newline)) - (save-excursion - (insert "[" (current-time-string) "]\n" msg) - (unless (bolp) - (newline)) - (when extra - (insert extra) - (unless (bolp) - (newline))))) - (error "%s%s" msg (if extra " (see *Notmuch errors* for more details)" ""))) - -(defun notmuch-check-async-exit-status (proc msg &optional command err) - "If PROC exited abnormally, pop up an error buffer and signal an error. - -This is a wrapper around `notmuch-check-exit-status' for -asynchronous process sentinels. PROC and MSG must be the -arguments passed to the sentinel. COMMAND and ERR, if provided, -are passed to `notmuch-check-exit-status'. If COMMAND is not -provided, it is taken from `process-command'." - (let ((exit-status - (cl-case (process-status proc) - ((exit) (process-exit-status proc)) - ((signal) msg)))) - (when exit-status - (notmuch-check-exit-status exit-status - (or command (process-command proc)) - nil err)))) - -(defun notmuch-check-exit-status (exit-status command &optional output err) - "If EXIT-STATUS is non-zero, pop up an error buffer and signal an error. - -If EXIT-STATUS is non-zero, pop up a notmuch error buffer -describing the error and signal an Elisp error. EXIT-STATUS must -be a number indicating the exit status code of a process or a -string describing the signal that terminated the process (such as -returned by `call-process'). COMMAND must be a list giving the -command and its arguments. OUTPUT, if provided, is a string -giving the output of command. ERR, if provided, is the error -output of command. OUTPUT and ERR will be included in the error -message." - (cond - ((eq exit-status 0) t) - ((eq exit-status 20) - (notmuch-logged-error "notmuch CLI version mismatch -Emacs requested an older output format than supported by the notmuch CLI. -You may need to restart Emacs or upgrade your notmuch Emacs package.")) - ((eq exit-status 21) - (notmuch-logged-error "notmuch CLI version mismatch -Emacs requested a newer output format than supported by the notmuch CLI. -You may need to restart Emacs or upgrade your notmuch package.")) - (t - (pcase-let* - ((`(,command . ,args) command) - (command (if (equal (file-name-nondirectory command) - notmuch-command) - notmuch-command - command)) - (command-string - (mapconcat (lambda (arg) - (shell-quote-argument - (cond ((stringp arg) arg) - ((symbolp arg) (symbol-name arg)) - (t "*UNKNOWN ARGUMENT*")))) - (cons command args) - " ")) - (extra - (concat "command: " command-string "\n" - (if (integerp exit-status) - (format "exit status: %s\n" exit-status) - (format "exit signal: %s\n" exit-status)) - (and err (concat "stderr:\n" err)) - (and output (concat "stdout:\n" output))))) - (if err - ;; We have an error message straight from the CLI. - (notmuch-logged-error - (replace-regexp-in-string "[ \n\r\t\f]*\\'" "" err) extra) - ;; We only have combined output from the CLI; don't inundate - ;; the user with it. Mimic `process-lines'. - (notmuch-logged-error (format "%s exited with status %s" - command exit-status) - extra)) - ;; `notmuch-logged-error' does not return. - )))) - -(defmacro notmuch--apply-with-env (func &rest args) - `(let ((default-directory "~")) - (apply ,func ,@args))) - -(defun notmuch--process-lines (program &rest args) - "Wrap process-lines, binding DEFAULT-DIRECTORY to a safe -default" - (notmuch--apply-with-env #'process-lines program args)) - -(defun notmuch--make-process (&rest args) - "Wrap make-process, binding DEFAULT-DIRECTORY to a safe -default" - (notmuch--apply-with-env #'make-process args)) - -(defun notmuch--call-process-region (start end program - &optional delete buffer display - &rest args) - "Wrap call-process-region, binding DEFAULT-DIRECTORY to a safe -default" - (notmuch--apply-with-env - #'call-process-region start end program delete buffer display args)) - -(defun notmuch--call-process (program &optional infile destination display &rest args) - "Wrap call-process, binding DEFAULT-DIRECTORY to a safe default" - (notmuch--apply-with-env #'call-process program infile destination display args)) - -(defun notmuch-call-notmuch--helper (destination args) - "Helper for synchronous notmuch invocation commands. - -This wraps `call-process'. DESTINATION has the same meaning as -for `call-process'. ARGS is as described for -`notmuch-call-notmuch-process'." - (let (stdin-string) - (while (keywordp (car args)) - (cl-case (car args) - (:stdin-string (setq stdin-string (cadr args)) - (setq args (cddr args))) - (otherwise - (error "Unknown keyword argument: %s" (car args))))) - (if (null stdin-string) - (apply #'notmuch--call-process notmuch-command nil destination nil args) - (insert stdin-string) - (apply #'notmuch--call-process-region (point-min) (point-max) - notmuch-command t destination nil args)))) - -(defun notmuch-call-notmuch-process (&rest args) - "Synchronously invoke `notmuch-command' with ARGS. - -The caller may provide keyword arguments before ARGS. Currently -supported keyword arguments are: - - :stdin-string STRING - Write STRING to stdin - -If notmuch exits with a non-zero status, output from the process -will appear in a buffer named \"*Notmuch errors*\" and an error -will be signaled." - (with-temp-buffer - (let ((status (notmuch-call-notmuch--helper t args))) - (notmuch-check-exit-status status (cons notmuch-command args) - (buffer-string))))) - -(defun notmuch-call-notmuch-sexp (&rest args) - "Invoke `notmuch-command' with ARGS and return the parsed S-exp output. - -This is equivalent to `notmuch-call-notmuch-process', but parses -notmuch's output as an S-expression and returns the parsed value. -Like `notmuch-call-notmuch-process', if notmuch exits with a -non-zero status, this will report its output and signal an -error." - (with-temp-buffer - (let ((err-file (make-temp-file "nmerr"))) - (unwind-protect - (let ((status (notmuch-call-notmuch--helper (list t err-file) args)) - (err (with-temp-buffer - (insert-file-contents err-file) - (unless (eobp) - (buffer-string))))) - (notmuch-check-exit-status status (cons notmuch-command args) - (buffer-string) err) - (goto-char (point-min)) - (read (current-buffer))) - (delete-file err-file))))) - -(defun notmuch-start-notmuch (name buffer sentinel &rest args) - "Start and return an asynchronous notmuch command. - -This starts and returns an asynchronous process running -`notmuch-command' with ARGS. The exit status is checked via -`notmuch-check-async-exit-status'. Output written to stderr is -redirected and displayed when the process exits (even if the -process exits successfully). NAME and BUFFER are the same as in -`start-process'. SENTINEL is a process sentinel function to call -when the process exits, or nil for none. The caller must *not* -invoke `set-process-sentinel' directly on the returned process, -as that will interfere with the handling of stderr and the exit -status." - (let* ((command (or (executable-find notmuch-command) - (error "Command not found: %s" notmuch-command))) - (err-buffer (generate-new-buffer " *notmuch-stderr*")) - (proc (notmuch--make-process - :name name - :buffer buffer - :command (cons command args) - :connection-type 'pipe - :stderr err-buffer)) - (err-proc (get-buffer-process err-buffer))) - (process-put proc 'err-buffer err-buffer) - (process-put proc 'sub-sentinel sentinel) - (set-process-sentinel proc #'notmuch-start-notmuch-sentinel) - (set-process-sentinel err-proc #'notmuch-start-notmuch-error-sentinel) - proc)) - -(defun notmuch-start-notmuch-sentinel (proc event) - "Process sentinel function used by `notmuch-start-notmuch'." - (let* ((err-buffer (process-get proc 'err-buffer)) - (err (and (buffer-live-p err-buffer) - (not (zerop (buffer-size err-buffer))) - (with-current-buffer err-buffer (buffer-string)))) - (sub-sentinel (process-get proc 'sub-sentinel))) - (condition-case err - (progn - ;; Invoke the sub-sentinel, if any - (when sub-sentinel - (funcall sub-sentinel proc event)) - ;; Check the exit status. This will signal an error if the - ;; exit status is non-zero. Don't do this if the process - ;; buffer is dead since that means Emacs killed the process - ;; and there's no point in telling the user that (but we - ;; still check for and report stderr output below). - (when (buffer-live-p (process-buffer proc)) - (notmuch-check-async-exit-status proc event nil err)) - ;; If that didn't signal an error, then any error output was - ;; really warning output. Show warnings, if any. - (let ((warnings - (and err - (with-current-buffer err-buffer - (goto-char (point-min)) - (end-of-line) - ;; Show first line; stuff remaining lines in the - ;; errors buffer. - (let ((l1 (buffer-substring (point-min) (point)))) - (skip-chars-forward "\n") - (cons l1 (and (not (eobp)) - (buffer-substring (point) - (point-max))))))))) - (when warnings - (notmuch-logged-error (car warnings) (cdr warnings))))) - (error - ;; Emacs behaves strangely if an error escapes from a sentinel, - ;; so turn errors into messages. - (message "%s" (error-message-string err)))))) - -(defun notmuch-start-notmuch-error-sentinel (proc _event) - (unless (process-live-p proc) - (let ((buffer (process-buffer proc))) - (when (buffer-live-p buffer) - (kill-buffer buffer))))) - -(defvar-local notmuch-show-process-crypto nil) - -(defun notmuch--run-show (search-terms &optional duplicate) - "Return a list of threads of messages matching SEARCH-TERMS. - -A thread is a forest or list of trees. A tree is a two element -list where the first element is a message, and the second element -is a possibly empty forest of replies." - (let ((args '("show" "--format=sexp" "--format-version=5"))) - (when notmuch-show-process-crypto - (setq args (append args '("--decrypt=true")))) - (when duplicate - (setq args (append args (list (format "--duplicate=%d" duplicate))))) - (setq args (append args search-terms)) - (apply #'notmuch-call-notmuch-sexp args))) - -;;; Generic Utilities - -(defun notmuch-interactive-region () - "Return the bounds of the current interactive region. - -This returns (BEG END), where BEG and END are the bounds of the -region if the region is active, or both `point' otherwise." - (if (region-active-p) - (list (region-beginning) (region-end)) - (list (point) (point)))) - -(define-obsolete-function-alias - 'notmuch-search-interactive-region - 'notmuch-interactive-region - "notmuch 0.29") - -(defun notmuch--inline-override-types () - "Override mm-inline-override-types to stop application/* -parts from being displayed unless the user has customized -it themselves." - (if (equal mm-inline-override-types - (eval (car (get 'mm-inline-override-types 'standard-value)))) - (cons "application/.*" mm-inline-override-types) - mm-inline-override-types)) -;;; _ - -(provide 'notmuch-lib) - -;;; notmuch-lib.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-lib.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-lib.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-logo.svg b/emacs/elpa/notmuch-20231006.2337/notmuch-logo.svg @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" - viewbox="0 0 100 100" fill="#000" stroke-width="2"> - <circle cx="50" cy="5" r="5" /> - <g transform="translate(50 20) rotate(20)"> - <circle cx="-47" cy="0" r="3" /> - <circle cx="47" cy="0" r="3" /> - <path d="M-47 -1 L0 -3 L47 -1 L47 1 L0 3 L-47 1 Z" /> - </g> - <path d="M49 4 L45 88 - A5 5 0 0 1 40 93 L20 93 A5 5 0 0 0 15 100 - L85 100 - A5 5 0 0 0 80 93 L60 93 A5 5 0 0 1 55 88 - L55 90 L51 4 Z" /> - <g fill="#fff" stroke="#000"> - <rect x="7" y="33" width="30" height="18" /> - <line x1="7" y1="51" x2="18" y2="41" /> - <line x1="37" y1="51" x2="26" y2="41" /> - <polyline points="7 33 22 44 37 33" fill="none" /> - </g> - <path d="M-18 0 A24 20 0 0 0 18 0" transform="translate(22 51.0)" /> - <path d="M-18 0 A24 20 0 0 0 18 0" transform="translate(78 71.5)" /> - <g fill="none" stroke="#000"> - <path d="M9 53.0 l 12 -42 l 2 0 l 12 42" /> - <path d="M91 73.5 l-12 -42 l-2 0 l-12 42" /> - </g> -</svg> diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-maildir-fcc.el b/emacs/elpa/notmuch-20231006.2337/notmuch-maildir-fcc.el @@ -1,362 +0,0 @@ -;;; notmuch-maildir-fcc.el --- inserting using a fcc handler -*- lexical-binding: t -*- - -;; Copyright © Jesse Rosenthal -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: Jesse Rosenthal <jrosenthal@jhu.edu> - -;;; Code: - -(require 'seq) - -(require 'message) - -(require 'notmuch-lib) - -(defvar notmuch-maildir-fcc-count 0) - -;;; Options - -(defcustom notmuch-fcc-dirs "sent" - "Determines the Fcc Header which says where to save outgoing mail. - -Three types of values are permitted: - -- nil: no Fcc header is added, - -- a string: the value of `notmuch-fcc-dirs' is the Fcc header to - be used. - -- an alist: the folder is chosen based on the From address of - the current message according to an alist mapping regular - expressions to folders or nil: - - ((\"Sebastian@SSpaeth.de\" . \"privat\") - (\"spaetz@sspaeth.de\" . \"OUTBOX.OSS\") - (\".*\" . \"defaultinbox\")) - - If none of the regular expressions match the From address, or - if the cdr of the matching entry is nil, then no Fcc header - will be added. - -If `notmuch-maildir-use-notmuch-insert' is set (the default) then -the header should be of the form \"folder +tag1 -tag2\" where -folder is the folder (relative to the notmuch mailstore) to store -the message in, and tag1 and tag2 are tag changes to apply to the -stored message. This string is split using `split-string-and-unquote', -so a folder name containing spaces can be specified by -quoting each space with an immediately preceding backslash -or surrounding the entire folder name in double quotes. - -If `notmuch-maildir-use-notmuch-insert' is nil then the Fcc -header should be the directory where the message should be -saved. A relative directory will be understood to specify a -directory within the notmuch mail store, (as set by the -database.path option in the notmuch configuration file). - -In all cases you will be prompted to create the folder or -directory if it does not exist yet when sending a mail." - - :type '(choice - (const :tag "No FCC header" nil) - (string :tag "A single folder") - (repeat :tag "A folder based on the From header" - (cons regexp (choice (const :tag "No FCC header" nil) - (string :tag "Folder"))))) - :require 'notmuch-fcc-initialization - :group 'notmuch-send) - -(defcustom notmuch-maildir-use-notmuch-insert t - "Should fcc use notmuch insert instead of simple fcc." - :type '(choice :tag "Fcc Method" - (const :tag "Use notmuch insert" t) - (const :tag "Use simple fcc" nil)) - :group 'notmuch-send) - -;;; Functions which set up the fcc header in the message buffer. - -(defun notmuch-fcc-header-setup () - "Add an Fcc header to the current message buffer. - -If the Fcc header is already set, then keep it as-is. -Otherwise set it according to `notmuch-fcc-dirs'." - (let ((subdir - (cond - ((or (not notmuch-fcc-dirs) - (message-field-value "Fcc")) - ;; Nothing set or an existing header. - nil) - ((stringp notmuch-fcc-dirs) - notmuch-fcc-dirs) - ((and (listp notmuch-fcc-dirs) - (stringp (car notmuch-fcc-dirs))) - ;; Old style - no longer works. - (error "Invalid `notmuch-fcc-dirs' setting (old style)")) - ((listp notmuch-fcc-dirs) - (if-let ((match (seq-some (let ((from (message-field-value "From"))) - (pcase-lambda (`(,regexp . ,folder)) - (and (string-match-p regexp from) - (cons t folder)))) - notmuch-fcc-dirs))) - (cdr match) - (message "No Fcc header added.") - nil)) - (t - (error "Invalid `notmuch-fcc-dirs' setting (neither string nor list)"))))) - (when subdir - (if notmuch-maildir-use-notmuch-insert - (notmuch-maildir-add-notmuch-insert-style-fcc-header subdir) - (notmuch-maildir-add-file-style-fcc-header subdir))))) - -(defun notmuch-maildir-add-notmuch-insert-style-fcc-header (subdir) - ;; Notmuch insert does not accept absolute paths, so check the user - ;; really want this header inserted. - (when (or (not (= (elt subdir 0) ?/)) - (y-or-n-p (format "Fcc header %s is an absolute path %s %s" subdir - "and notmuch insert is requested." - "Insert header anyway? "))) - (message-add-header (concat "Fcc: " subdir)))) - -(defun notmuch-maildir-add-file-style-fcc-header (subdir) - (message-add-header - (concat "Fcc: " - (file-truename - ;; If the resulting directory is not an absolute path, - ;; prepend the standard notmuch database path. - (if (= (elt subdir 0) ?/) - subdir - (concat (notmuch-database-path) "/" subdir)))))) - -;;; Functions for saving a message using either method. - -(defmacro with-temporary-notmuch-message-buffer (&rest body) - "Set-up a temporary copy of the current message-mode buffer." - `(let ((case-fold-search t) - (buf (current-buffer)) - (mml-externalize-attachments message-fcc-externalize-attachments)) - (with-current-buffer (get-buffer-create " *message temp*") - (message-clone-locals buf) ;; for message-encoded-mail-cache - (erase-buffer) - (insert-buffer-substring buf) - ,@body))) - -(defun notmuch-maildir-setup-message-for-saving () - "Setup message for saving. - -This should be called on a temporary copy. -This is taken from the function message-do-fcc." - (if (not message-encoded-mail-cache) - (message-encode-message-body) - (erase-buffer) - (insert message-encoded-mail-cache)) - (save-restriction - (message-narrow-to-headers) - (mail-encode-encoded-word-buffer)) - (goto-char (point-min)) - (when (re-search-forward - (concat "^" (regexp-quote mail-header-separator) "$") - nil t) - (replace-match "" t t ))) - -(defun notmuch-maildir-message-do-fcc () - "Process Fcc headers in the current buffer. - -This is a rearranged version of message mode's message-do-fcc." - (let (files file) - (save-excursion - (save-restriction - (message-narrow-to-headers) - (setq file (message-fetch-field "fcc" t))) - (when file - (with-temporary-notmuch-message-buffer - (notmuch-maildir-setup-message-for-saving) - (save-restriction - (message-narrow-to-headers) - (while (setq file (message-fetch-field "fcc" t)) - (push file files) - (message-remove-header "fcc" nil t))) - ;; Process FCC operations. - (mapc #'notmuch-fcc-handler files) - (kill-buffer (current-buffer))))))) - -(defun notmuch-fcc-handler (fcc-header) - "Store message with notmuch insert or normal (file) fcc. - -If `notmuch-maildir-use-notmuch-insert' is set then store the -message using notmuch insert. Otherwise store the message using -normal fcc." - (message "Doing Fcc...") - (if notmuch-maildir-use-notmuch-insert - (notmuch-maildir-fcc-with-notmuch-insert fcc-header) - (notmuch-maildir-fcc-file-fcc fcc-header)) - (message "Doing Fcc...done")) - -;;; Functions for saving a message using notmuch insert. - -(defun notmuch-maildir-notmuch-insert-current-buffer (folder &optional create tags) - "Use notmuch insert to put the current buffer in the database. - -This inserts the current buffer as a message into the notmuch -database in folder FOLDER. If CREATE is non-nil it will supply -the --create-folder flag to create the folder if necessary. TAGS -should be a list of tag changes to apply to the inserted message." - (apply 'notmuch-call-notmuch-process - :stdin-string (buffer-string) "insert" - (append (and create (list "--create-folder")) - (list (concat "--folder=" folder)) - tags))) - -(defun notmuch-maildir-fcc-with-notmuch-insert (fcc-header &optional create) - "Store message with notmuch insert. - -The fcc-header should be of the form \"folder +tag1 -tag2\" where -folder is the folder (relative to the notmuch mailstore) to store -the message in, and tag1 and tag2 are tag changes to apply to the -stored message. This string is split using `split-string-and-unquote', -so a folder name containing spaces can be specified by -quoting each space with an immediately preceding backslash -or surrounding the entire folder name in double quotes. - -If CREATE is non-nil then create the folder if necessary." - (pcase-let ((`(,folder . ,tags) - (split-string-and-unquote fcc-header))) - (condition-case nil - (notmuch-maildir-notmuch-insert-current-buffer folder create tags) - ;; Since there are many reasons notmuch insert could fail, e.g., - ;; locked database, non-existent folder (which could be due to a - ;; typo, or just the user want a new folder, let the user decide - ;; how to deal with it. - (error - (let ((response (read-char-choice "Insert failed: \ -\(r)etry, (c)reate folder, (i)gnore, or (e)dit the header? " '(?r ?c ?i ?e)))) - (cl-case response - (?r (notmuch-maildir-fcc-with-notmuch-insert fcc-header)) - (?c (notmuch-maildir-fcc-with-notmuch-insert fcc-header t)) - (?i t) - (?e (notmuch-maildir-fcc-with-notmuch-insert - (read-from-minibuffer "Fcc header: " fcc-header))))))))) - -;;; Functions for saving a message using file fcc. - -(defun notmuch-maildir-fcc-host-fixer (hostname) - (replace-regexp-in-string "/\\|:" - (lambda (s) - (cond ((string-equal s "/") "\\057") - ((string-equal s ":") "\\072") - (t s))) - hostname - t - t)) - -(defun notmuch-maildir-fcc-make-uniq-maildir-id () - (let* ((ftime (float-time)) - (microseconds (mod (* 1000000 ftime) 1000000)) - (hostname (notmuch-maildir-fcc-host-fixer (system-name)))) - (cl-incf notmuch-maildir-fcc-count) - (format "%d.%d_%d_%d.%s" - ftime - (emacs-pid) - microseconds - notmuch-maildir-fcc-count - hostname))) - -(defun notmuch-maildir-fcc-dir-is-maildir-p (dir) - (and (file-exists-p (concat dir "/cur/")) - (file-exists-p (concat dir "/new/")) - (file-exists-p (concat dir "/tmp/")))) - -(defun notmuch-maildir-fcc-create-maildir (path) - (cond ((or (not (file-exists-p path)) (file-directory-p path)) - (make-directory (concat path "/cur/") t) - (make-directory (concat path "/new/") t) - (make-directory (concat path "/tmp/") t)) - ((file-regular-p path) - (error "%s is a file. Can't create maildir." path)) - (t - (error "I don't know how to create a maildir here")))) - -(defun notmuch-maildir-fcc-save-buffer-to-tmp (destdir) - "Returns the msg id of the message written to the temp directory -if successful, nil if not." - (let ((msg-id (notmuch-maildir-fcc-make-uniq-maildir-id))) - (while (file-exists-p (concat destdir "/tmp/" msg-id)) - (setq msg-id (notmuch-maildir-fcc-make-uniq-maildir-id))) - (cond ((notmuch-maildir-fcc-dir-is-maildir-p destdir) - (write-file (concat destdir "/tmp/" msg-id)) - msg-id) - (t - (error "Can't write to %s. Not a maildir." destdir))))) - -(defun notmuch-maildir-fcc-move-tmp-to-new (destdir msg-id) - (add-name-to-file - (concat destdir "/tmp/" msg-id) - (concat destdir "/new/" msg-id ":2,"))) - -(defun notmuch-maildir-fcc-move-tmp-to-cur (destdir msg-id &optional mark-seen) - (add-name-to-file - (concat destdir "/tmp/" msg-id) - (concat destdir "/cur/" msg-id ":2," (and mark-seen "S")))) - -(defun notmuch-maildir-fcc-file-fcc (fcc-header) - "Write the message to the file specified by FCC-HEADER. - -If that fails, then offer the user a chance to correct the header -or filesystem." - (if (notmuch-maildir-fcc-dir-is-maildir-p fcc-header) - (notmuch-maildir-fcc-write-buffer-to-maildir fcc-header t) - ;; The fcc-header is not a valid maildir see if the user wants to - ;; fix it in some way. - (let* ((prompt (format "Fcc %s is not a maildir: \ -\(r)etry, (c)reate folder, (i)gnore, or (e)dit the header? " fcc-header)) - (response (read-char-choice prompt '(?r ?c ?i ?e)))) - (cl-case response - (?r (notmuch-maildir-fcc-file-fcc fcc-header)) - (?c (if (file-writable-p fcc-header) - (notmuch-maildir-fcc-create-maildir fcc-header) - (message "No permission to create %s." fcc-header) - (sit-for 2)) - (notmuch-maildir-fcc-file-fcc fcc-header)) - (?i t) - (?e (notmuch-maildir-fcc-file-fcc - (read-from-minibuffer "Fcc header: " fcc-header))))))) - -(defun notmuch-maildir-fcc-write-buffer-to-maildir (destdir &optional mark-seen) - "Write the current buffer to maildir destdir. - -If mark-seen is non-nil, then write it to \"cur/\", and mark it -as read, otherwise write it to \"new/\". Return t if successful, -and nil otherwise." - (let ((orig-buffer (buffer-name))) - (with-temp-buffer - (insert-buffer-substring orig-buffer) - (catch 'link-error - (let ((msg-id (notmuch-maildir-fcc-save-buffer-to-tmp destdir))) - (when msg-id - (condition-case nil - (if mark-seen - (notmuch-maildir-fcc-move-tmp-to-cur destdir msg-id t) - (notmuch-maildir-fcc-move-tmp-to-new destdir msg-id)) - (file-already-exists - (throw 'link-error nil)))) - (delete-file (concat destdir "/tmp/" msg-id)))) - t))) - -;;; _ - -(provide 'notmuch-maildir-fcc) - -;;; notmuch-maildir-fcc.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-maildir-fcc.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-maildir-fcc.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-message.el b/emacs/elpa/notmuch-20231006.2337/notmuch-message.el @@ -1,76 +0,0 @@ -;;; notmuch-message.el --- message-mode functions specific to notmuch -*- lexical-binding: t -*- -;; -;; Copyright © Jesse Rosenthal -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: Jesse Rosenthal <jrosenthal@jhu.edu> - -;;; Code: - -(require 'cl-lib) -(require 'pcase) -(require 'subr-x) - -(require 'message) -(require 'notmuch-tag) - -(defcustom notmuch-message-replied-tags '("+replied") - "List of tag changes to apply to a message when it has been replied to. - -Tags starting with \"+\" (or not starting with either \"+\" or -\"-\") in the list will be added, and tags starting with \"-\" -will be removed from the message being replied to. - -For example, if you wanted to add a \"replied\" tag and remove -the \"inbox\" and \"todo\" tags, you would set: - (\"+replied\" \"-inbox\" \"-todo\")" - :type '(repeat string) - :group 'notmuch-send) - -(defcustom notmuch-message-forwarded-tags '("+forwarded") - "List of tag changes to apply to a message when it has been forwarded. - -Tags starting with \"+\" (or not starting with either \"+\" or -\"-\") in the list will be added, and tags starting with \"-\" -will be removed from the message being forwarded. - -For example, if you wanted to add a \"forwarded\" tag and remove -the \"inbox\" tag, you would set: - (\"+forwarded\" \"-inbox\")" - :type '(repeat string) - :group 'notmuch-send) - -(defvar-local notmuch-message-queued-tag-changes nil - "List of tag changes to be applied when sending a message. - -A list of queries and tag changes that are to be applied to them -when the message that was composed in the current buffer is being -send. Each item in this list is a list of strings, where the -first is a notmuch query and the rest are the tag changes to be -applied to the matching messages.") - -(defun notmuch-message-apply-queued-tag-changes () - ;; Apply the tag changes queued in the buffer-local variable - ;; notmuch-message-queued-tag-changes. - (pcase-dolist (`(,query . ,tags) notmuch-message-queued-tag-changes) - (notmuch-tag query tags))) - -(add-hook 'message-send-hook 'notmuch-message-apply-queued-tag-changes) - -(provide 'notmuch-message) - -;;; notmuch-message.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-message.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-message.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-mua.el b/emacs/elpa/notmuch-20231006.2337/notmuch-mua.el @@ -1,651 +0,0 @@ -;;; notmuch-mua.el --- emacs style mail-user-agent -*- lexical-binding: t -*- -;; -;; Copyright © David Edmondson -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: David Edmondson <dme@dme.org> - -;;; Code: - -(eval-when-compile (require 'subr-x)) - -(require 'message) -(require 'gmm-utils) -(require 'mm-view) -(require 'format-spec) - -(require 'notmuch-lib) -(require 'notmuch-address) -(require 'notmuch-draft) -(require 'notmuch-message) - -(declare-function notmuch-show-insert-body "notmuch-show" (msg body depth)) -(declare-function notmuch-fcc-header-setup "notmuch-maildir-fcc" ()) -(declare-function notmuch-maildir-message-do-fcc "notmuch-maildir-fcc" ()) -(declare-function notmuch-draft-postpone "notmuch-draft" ()) -(declare-function notmuch-draft-save "notmuch-draft" ()) - -(defvar notmuch-show-indent-multipart) -(defvar notmuch-show-insert-header-p-function) -(defvar notmuch-show-max-text-part-size) -(defvar notmuch-show-insert-text/plain-hook) - -;;; Options - -(defcustom notmuch-mua-send-hook nil - "Hook run before sending messages." - :type 'hook - :group 'notmuch-send - :group 'notmuch-hooks) - -(defcustom notmuch-mua-compose-in 'current-window - "Where to create the mail buffer used to compose a new message. -Possible values are `current-window' (default), `new-window' and -`new-frame'. If set to `current-window', the mail buffer will be -displayed in the current window, so the old buffer will be -restored when the mail buffer is killed. If set to `new-window' -or `new-frame', the mail buffer will be displayed in a new -window/frame that will be destroyed when the buffer is killed. -You may want to customize `message-kill-buffer-on-exit' -accordingly." - :group 'notmuch-send - :type '(choice (const :tag "Compose in the current window" current-window) - (const :tag "Compose mail in a new window" new-window) - (const :tag "Compose mail in a new frame" new-frame))) - -(defcustom notmuch-mua-user-agent-function nil - "Function used to generate a `User-Agent:' string. -If this is `nil' then no `User-Agent:' will be generated." - :type '(choice (const :tag "No user agent string" nil) - (const :tag "Full" notmuch-mua-user-agent-full) - (const :tag "Notmuch" notmuch-mua-user-agent-notmuch) - (const :tag "Emacs" notmuch-mua-user-agent-emacs) - (function :tag "Custom user agent function" - :value notmuch-mua-user-agent-full)) - :group 'notmuch-send) - -(defcustom notmuch-mua-hidden-headers nil - "Headers that are added to the `message-mode' hidden headers list." - :type '(repeat string) - :group 'notmuch-send) - -(defcustom notmuch-identities nil - "Identities that can be used as the From: address when composing a new message. - -If this variable is left unset, then a list will be constructed from the -name and addresses configured in the notmuch configuration file." - :type '(repeat string) - :group 'notmuch-send) - -(defcustom notmuch-always-prompt-for-sender nil - "Always prompt for the From: address when composing or forwarding a message. - -This is not taken into account when replying to a message, because in that case -the From: header is already filled in by notmuch." - :type 'boolean - :group 'notmuch-send) - -(defgroup notmuch-reply nil - "Replying to messages in notmuch." - :group 'notmuch) - -(defcustom notmuch-mua-cite-function 'message-cite-original - "Function for citing an original message. - -Predefined functions include `message-cite-original' and -`message-cite-original-without-signature'. Note that these -functions use `mail-citation-hook' if that is non-nil." - :type '(radio (function-item message-cite-original) - (function-item message-cite-original-without-signature) - (function-item sc-cite-original) - (function :tag "Other")) - :link '(custom-manual "(message)Insertion Variables") - :group 'notmuch-reply) - -(defcustom notmuch-mua-reply-insert-header-p-function - 'notmuch-show-reply-insert-header-p-never - "Function to decide which parts get a header when replying. - -This function specifies which parts of a mime message with -multiple parts get a header." - :type '(radio (const :tag "No part headers" - notmuch-show-reply-insert-header-p-never) - (const :tag "All except multipart/* and hidden parts" - notmuch-show-reply-insert-header-p-trimmed) - (const :tag "Only for included text parts" - notmuch-show-reply-insert-header-p-minimal) - (const :tag "Exactly as in show view" - notmuch-show-insert-header-p) - (function :tag "Other")) - :group 'notmuch-reply) - -(defcustom notmuch-mua-attachment-regexp - "\\b\\(attache\?ment\\|attached\\|attach\\|pi[èe]ce\s+jointe?\\)\\b" - "Message body text indicating that an attachment is expected. - -This is not used unless `notmuch-mua-attachment-check' is added -to `notmuch-mua-send-hook'." - :type 'regexp - :group 'notmuch-send) - -;;; Various functions - -(defun notmuch-mua-attachment-check () - "Signal an error an attachement is expected but missing. - -Signal an error if the message text indicates that an attachment -is expected but no MML referencing an attachment is found. - -Typically this is added to `notmuch-mua-send-hook'." - (when (and - ;; When the message mentions attachment... - (save-excursion - (message-goto-body) - ;; Limit search from reaching other possible parts of the message - (let ((search-limit (search-forward "\n<#" nil t))) - (message-goto-body) - (cl-loop while (re-search-forward notmuch-mua-attachment-regexp - search-limit t) - ;; For every instance of the "attachment" string - ;; found, examine the text properties. If the text - ;; has either a `face' or `syntax-table' property - ;; then it is quoted text and should *not* cause the - ;; user to be asked about a missing attachment. - if (let ((props (text-properties-at (match-beginning 0)))) - (not (or (memq 'syntax-table props) - (memq 'face props)))) - return t - finally return nil))) - ;; ...but doesn't have a part with a filename... - (save-excursion - (message-goto-body) - (not (re-search-forward "^<#part [^>]*filename=" nil t))) - ;; ...and that's not okay... - (not (y-or-n-p "Attachment mentioned, but no attachment - is that okay?"))) - ;; ...signal an error. - (error "Missing attachment"))) - -(defun notmuch-mua-get-switch-function () - "Get a switch function according to `notmuch-mua-compose-in'." - (pcase notmuch-mua-compose-in - ('current-window 'switch-to-buffer) - ('new-window 'switch-to-buffer-other-window) - ('new-frame 'switch-to-buffer-other-frame) - (_ (error "Invalid value for `notmuch-mua-compose-in'")))) - -(defun notmuch-mua-maybe-set-window-dedicated () - "Set the selected window as dedicated according to `notmuch-mua-compose-in'." - (when (or (eq notmuch-mua-compose-in 'new-frame) - (eq notmuch-mua-compose-in 'new-window)) - (set-window-dedicated-p (selected-window) t))) - -(defun notmuch-mua-user-agent-full () - "Generate a `User-Agent:' string suitable for notmuch." - (concat (notmuch-mua-user-agent-notmuch) - " " - (notmuch-mua-user-agent-emacs))) - -(defun notmuch-mua-user-agent-notmuch () - "Generate a `User-Agent:' string suitable for notmuch." - (let ((notmuch-version (if (string= notmuch-emacs-version "unknown") - (notmuch-cli-version) - notmuch-emacs-version))) - (concat "Notmuch/" notmuch-version " (https://notmuchmail.org)"))) - -(defun notmuch-mua-user-agent-emacs () - "Generate a `User-Agent:' string suitable for notmuch." - (concat "Emacs/" emacs-version " (" system-configuration ")")) - -(defun notmuch-mua-add-more-hidden-headers () - "Add some headers to the list that are hidden by default." - (mapc (lambda (header) - (unless (member header message-hidden-headers) - (push header message-hidden-headers))) - notmuch-mua-hidden-headers)) - -(defun notmuch-mua-reply-crypto (parts) - "Add mml sign-encrypt flag if any part of original message is encrypted." - (cl-loop for part in parts - for type = (plist-get part :content-type) - if (notmuch-match-content-type type "multipart/encrypted") - do (mml-secure-message-sign-encrypt) - else if (notmuch-match-content-type type "multipart/*") - do (notmuch-mua-reply-crypto (plist-get part :content)))) - -;; There is a bug in Emacs' message.el that results in a newline -;; not being inserted after the References header, so the next header -;; is concatenated to the end of it. This function fixes the problem, -;; while guarding against the possibility that some current or future -;; version of emacs has the bug fixed. -(defun notmuch-mua-insert-references (original-func header references) - (funcall original-func header references) - (unless (bolp) (insert "\n"))) - -;;; Mua reply - -(defun notmuch-mua-reply (query-string &optional sender reply-all duplicate) - (let* ((duparg (and duplicate (list (format "--duplicate=%d" duplicate)))) - (args `("reply" "--format=sexp" "--format-version=5" ,@duparg)) - (process-crypto notmuch-show-process-crypto) - reply - original) - (when process-crypto - (setq args (append args '("--decrypt=true")))) - (if reply-all - (setq args (append args '("--reply-to=all"))) - (setq args (append args '("--reply-to=sender")))) - (setq args (append args (list query-string))) - ;; Get the reply object as SEXP, and parse it into an elisp object. - (setq reply (apply #'notmuch-call-notmuch-sexp args)) - ;; Extract the original message to simplify the following code. - (setq original (plist-get reply :original)) - ;; Extract the headers of both the reply and the original message. - (let* ((original-headers (plist-get original :headers)) - (reply-headers (plist-get reply :reply-headers))) - ;; If sender is non-nil, set the From: header to its value. - (when sender - (plist-put reply-headers :From sender)) - (let - ;; Overlay the composition window on that being used to read - ;; the original message. - ((same-window-regexps '("\\*mail .*"))) - ;; We modify message-header-format-alist to get around - ;; a bug in message.el. See the comment above on - ;; notmuch-mua-insert-references. - (let ((message-header-format-alist - (cl-loop for pair in message-header-format-alist - if (eq (car pair) 'References) - collect (cons 'References - (apply-partially - 'notmuch-mua-insert-references - (cdr pair))) - else - collect pair))) - (notmuch-mua-mail (plist-get reply-headers :To) - (notmuch-sanitize (plist-get reply-headers :Subject)) - (notmuch-headers-plist-to-alist reply-headers) - nil (notmuch-mua-get-switch-function)))) - ;; Create a buffer-local queue for tag changes triggered when - ;; sending the reply. - (when notmuch-message-replied-tags - (setq notmuch-message-queued-tag-changes - (list (cons query-string notmuch-message-replied-tags)))) - ;; Insert the message body - but put it in front of the signature - ;; if one is present, and after any other content - ;; message*setup-hooks may have added to the message body already. - (save-restriction - (message-goto-body) - (narrow-to-region (point) (point-max)) - (goto-char (point-max)) - (if (re-search-backward message-signature-separator nil t) - (when message-signature-insert-empty-line - (forward-line -1)) - (goto-char (point-max)))) - (let ((from (plist-get original-headers :From)) - (date (plist-get original-headers :Date)) - (start (point))) - ;; notmuch-mua-cite-function constructs a citation line based - ;; on the From and Date headers of the original message, which - ;; are assumed to be in the buffer. - (insert "From: " from "\n") - (insert "Date: " date "\n\n") - (insert - (with-temp-buffer - (let - ;; Don't attempt to clean up messages, excerpt - ;; citations, etc. in the original message before - ;; quoting. - ((notmuch-show-insert-text/plain-hook nil) - ;; Don't omit long parts. - (notmuch-show-max-text-part-size 0) - ;; Insert headers for parts as appropriate for replying. - (notmuch-show-insert-header-p-function - notmuch-mua-reply-insert-header-p-function) - ;; Ensure that any encrypted parts are - ;; decrypted during the generation of the reply - ;; text. - (notmuch-show-process-crypto process-crypto) - ;; Don't indent multipart sub-parts. - (notmuch-show-indent-multipart nil) - ;; Stop certain mime types from being inlined - (mm-inline-override-types (notmuch--inline-override-types))) - ;; We don't want sigstatus buttons (an information leak and usually wrong anyway). - (cl-letf (((symbol-function 'notmuch-crypto-insert-sigstatus-button) #'ignore) - ((symbol-function 'notmuch-crypto-insert-encstatus-button) #'ignore)) - (notmuch-show-insert-body original (plist-get original :body) 0) - (buffer-substring-no-properties (point-min) (point-max)))))) - (set-mark (point)) - (goto-char start) - ;; Quote the original message according to the user's configured style. - (funcall notmuch-mua-cite-function))) - ;; Crypto processing based crypto content of the original message - (when process-crypto - (notmuch-mua-reply-crypto (plist-get original :body)))) - ;; Push mark right before signature, if any. - (message-goto-signature) - (unless (eobp) - (end-of-line -1)) - (push-mark) - (message-goto-body) - (set-buffer-modified-p nil)) - -;;; Mode and keymap - -(defvar notmuch-message-mode-map - (let ((map (make-sparse-keymap))) - (define-key map [remap message-send-and-exit] #'notmuch-mua-send-and-exit) - (define-key map [remap message-send] #'notmuch-mua-send) - (define-key map (kbd "C-c C-p") #'notmuch-draft-postpone) - (define-key map (kbd "C-x C-s") #'notmuch-draft-save) - map) - "Keymap for `notmuch-message-mode'.") - -(define-derived-mode notmuch-message-mode message-mode "Message[Notmuch]" - "Notmuch message composition mode. Mostly like `message-mode'." - (notmuch-address-setup)) - -(put 'notmuch-message-mode 'flyspell-mode-predicate 'mail-mode-flyspell-verify) - -;;; New messages - -(defun notmuch-mua-pop-to-buffer (name switch-function) - "Pop to buffer NAME, and warn if it already exists and is modified. -Like `message-pop-to-buffer' but enable `notmuch-message-mode' -instead of `message-mode' and SWITCH-FUNCTION is mandatory." - (let ((buffer (get-buffer name))) - (if (and buffer - (buffer-name buffer)) - (let ((window (get-buffer-window buffer 0))) - (if window - ;; Raise the frame already displaying the message buffer. - (progn - (select-frame-set-input-focus (window-frame window)) - (select-window window)) - (funcall switch-function buffer) - (set-buffer buffer)) - (when (buffer-modified-p) - (if (y-or-n-p "Message already being composed; erase? ") - (message nil) - (error "Message being composed")))) - (funcall switch-function name) - (set-buffer name)) - (erase-buffer) - (notmuch-message-mode))) - -(defun notmuch-mua--remove-dont-reply-to-names () - (when-let* ((nr (if (functionp message-dont-reply-to-names) - message-dont-reply-to-names - (gmm-regexp-concat message-dont-reply-to-names))) - (nr-filter - (if (functionp nr) - (lambda (mail) (and (not (funcall nr mail)) mail)) - (lambda (mail) (and (not (string-match-p nr mail)) mail))))) - (dolist (header '("To" "Cc")) - (when-let ((v (message-fetch-field header))) - (let* ((tokens (mapcar #'string-trim (message-tokenize-header v))) - (good-tokens (delq nil (mapcar nr-filter tokens))) - (addr (and good-tokens (mapconcat #'identity good-tokens ", ")))) - (message-replace-header header addr)))))) - -(defun notmuch-mua-mail (&optional to subject other-headers _continue - switch-function yank-action send-actions - return-action &rest _ignored) - "Invoke the notmuch mail composition window. - -The position of point when the function returns differs depending -on the values of TO and SUBJECT. If both are non-nil, point is -moved to the message's body. If SUBJECT is nil but TO isn't, -point is moved to the \"Subject:\" header. Otherwise, point is -moved to the \"To:\" header." - (interactive) - (when notmuch-mua-user-agent-function - (let ((user-agent (funcall notmuch-mua-user-agent-function))) - (unless (string-empty-p user-agent) - (push (cons 'User-Agent user-agent) other-headers)))) - (notmuch-mua-pop-to-buffer (message-buffer-name "mail" to) - (or switch-function - (notmuch-mua-get-switch-function))) - (let ((headers - (append - ;; The following is copied from `message-mail' - `((To . ,(or to "")) (Subject . ,(or subject ""))) - ;; C-h f compose-mail says that headers should be specified as - ;; (string . value); however all the rest of message expects - ;; headers to be symbols, not strings (eg message-header-format-alist). - ;; https://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html - ;; We need to convert any string input, eg from rmail-start-mail. - (dolist (h other-headers other-headers) - (when (stringp (car h)) - (setcar h (intern (capitalize (car h)))))))) - ;; Cause `message-setup-1' to do things relevant for mail, - ;; such as observe `message-default-mail-headers'. - (message-this-is-mail t)) - (unless (assq 'From headers) - (push (cons 'From (message-make-from - (notmuch-user-name) - (notmuch-user-primary-email))) - headers)) - (message-setup-1 headers yank-action send-actions return-action)) - (notmuch-fcc-header-setup) - (notmuch-mua--remove-dont-reply-to-names) - (message-sort-headers) - (message-hide-headers) - (set-buffer-modified-p nil) - (notmuch-mua-maybe-set-window-dedicated) - (cond - ((and to subject) (message-goto-body)) - (to (message-goto-subject)) - (t (message-goto-to)))) - -(defvar notmuch-mua-sender-history nil) - -(defun notmuch-mua-prompt-for-sender () - "Prompt for a sender from the user's configured identities." - (if notmuch-identities - (completing-read "Send mail from: " notmuch-identities - nil nil nil 'notmuch-mua-sender-history - (car notmuch-identities)) - (let* ((name (notmuch-user-name)) - (addrs (cons (notmuch-user-primary-email) - (notmuch-user-other-email))) - (address - (completing-read (concat "Sender address for " name ": ") addrs - nil nil nil 'notmuch-mua-sender-history - (car addrs)))) - (message-make-from name address)))) - -(put 'notmuch-mua-new-mail 'notmuch-prefix-doc "... and prompt for sender") -(defun notmuch-mua-new-mail (&optional prompt-for-sender) - "Compose new mail. - -If PROMPT-FOR-SENDER is non-nil, the user will be prompted for -the From: address first." - (interactive "P") - (let ((other-headers - (and (or prompt-for-sender notmuch-always-prompt-for-sender) - (list (cons 'From (notmuch-mua-prompt-for-sender)))))) - (notmuch-mua-mail nil nil other-headers nil (notmuch-mua-get-switch-function)))) - -(defun notmuch-mua-new-forward-messages (messages &optional prompt-for-sender) - "Compose a new message forwarding MESSAGES. - -If PROMPT-FOR-SENDER is non-nil, the user will be prompteed for -the From: address." - (let* ((other-headers - (and (or prompt-for-sender notmuch-always-prompt-for-sender) - (list (cons 'From (notmuch-mua-prompt-for-sender))))) - ;; Comes from the first message and is applied later. - forward-subject - ;; List of accumulated message-references of forwarded messages. - forward-references - ;; List of corresponding message-query. - forward-queries) - ;; Generate the template for the outgoing message. - (notmuch-mua-mail nil "" other-headers nil (notmuch-mua-get-switch-function)) - (save-excursion - ;; Insert all of the forwarded messages. - (mapc (lambda (id) - (let ((temp-buffer (get-buffer-create - (concat "*notmuch-fwd-raw-" id "*")))) - ;; Get the raw version of this message in the buffer. - (with-current-buffer temp-buffer - (erase-buffer) - (let ((coding-system-for-read 'no-conversion)) - (notmuch--call-process notmuch-command nil t nil - "show" "--format=raw" id)) - ;; Because we process the messages in reverse order, - ;; always generate a forwarded subject, then use the - ;; last (i.e. first) one. - (setq forward-subject (message-make-forward-subject)) - (push (message-fetch-field "Message-ID") forward-references) - (push id forward-queries)) - ;; Make a copy ready to be forwarded in the - ;; composition buffer. - (message-forward-make-body temp-buffer) - ;; Kill the temporary buffer. - (kill-buffer temp-buffer))) - ;; `message-forward-make-body' always puts the message at - ;; the top, so do them in reverse order. - (reverse messages)) - ;; Add in the appropriate subject. - (save-restriction - (message-narrow-to-headers) - (message-remove-header "Subject") - (message-add-header (concat "Subject: " forward-subject)) - (message-remove-header "References") - (message-add-header (concat "References: " - (mapconcat 'identity forward-references " ")))) - ;; Create a buffer-local queue for tag changes triggered when - ;; sending the message. - (when notmuch-message-forwarded-tags - (setq notmuch-message-queued-tag-changes - (cl-loop for id in forward-queries - collect - (cons id notmuch-message-forwarded-tags)))) - ;; `message-forward-make-body' shows the User-agent header. Hide - ;; it again. - (message-hide-headers) - (set-buffer-modified-p nil)))) - -(defun notmuch-mua-new-reply (query-string &optional prompt-for-sender reply-all duplicate) - "Compose a reply to the message identified by QUERY-STRING. - -If PROMPT-FOR-SENDER is non-nil, the user will be prompted for -the From: address first. If REPLY-ALL is non-nil, the message -will be addressed to all recipients of the source message. If -DUPLICATE is non-nil, based the reply on that duplicate file" - ;; `select-active-regions' is t by default. The reply insertion code - ;; sets the region to the quoted message to make it easy to delete - ;; (kill-region or C-w). These two things combine to put the quoted - ;; message in the primary selection. - ;; - ;; This is not what the user wanted and is a privacy risk (accidental - ;; pasting of the quoted message). We can avoid some of the problems - ;; by let-binding select-active-regions to nil. This fixes if the - ;; primary selection was previously in a non-emacs window but not if - ;; it was in an emacs window. To avoid the problem in the latter case - ;; we deactivate mark. - (let ((sender (and prompt-for-sender - (notmuch-mua-prompt-for-sender))) - (select-active-regions nil)) - (notmuch-mua-reply query-string sender reply-all duplicate) - (deactivate-mark))) - -;;; Checks - -(defun notmuch-mua-check-no-misplaced-secure-tag () - "Query user if there is a misplaced secure mml tag. - -Emacs message-send will (probably) ignore a secure mml tag unless -it is at the start of the body. Returns t if there is no such -tag, or the user confirms they mean it." - (save-excursion - (let ((body-start (progn (message-goto-body) (point)))) - (goto-char (point-max)) - (or - ;; We are always fine if there is no secure tag. - (not (search-backward "<#secure" nil t)) - ;; There is a secure tag, so it must be at the start of the - ;; body, with no secure tag earlier (i.e., in the headers). - (and (= (point) body-start) - (not (search-backward "<#secure" nil t))) - ;; The user confirms they means it. - (yes-or-no-p "\ -There is a <#secure> tag not at the start of the body. It is -likely that the message will be sent unsigned and unencrypted. -Really send? "))))) - -(defun notmuch-mua-check-secure-tag-has-newline () - "Query if the secure mml tag has a newline following it. - -Emacs message-send will (probably) ignore a correctly placed -secure mml tag unless it is followed by a newline. Returns t if -any secure tag is followed by a newline, or the user confirms -they mean it." - (save-excursion - (message-goto-body) - (or - ;; There is no (correctly placed) secure tag. - (not (looking-at "<#secure")) - ;; The secure tag is followed by a newline. - (looking-at "<#secure[^\n>]*>\n") - ;; The user confirms they means it. - (yes-or-no-p "\ -The <#secure> tag at the start of the body is not followed by a -newline. It is likely that the message will be sent unsigned and -unencrypted. Really send? ")))) - -;;; Finishing commands - -(defun notmuch-mua-send-common (arg &optional exit) - (interactive "P") - (run-hooks 'notmuch-mua-send-hook) - (when (and (notmuch-mua-check-no-misplaced-secure-tag) - (notmuch-mua-check-secure-tag-has-newline)) - (cl-letf (((symbol-function 'message-do-fcc) - #'notmuch-maildir-message-do-fcc)) - (if exit - (message-send-and-exit arg) - (message-send arg))))) - -(defun notmuch-mua-send-and-exit (&optional arg) - (interactive "P") - (notmuch-mua-send-common arg t)) - -(defun notmuch-mua-send (&optional arg) - (interactive "P") - (notmuch-mua-send-common arg)) - -(defun notmuch-mua-kill-buffer () - (interactive) - (message-kill-buffer)) - -;;; _ - -(define-mail-user-agent 'notmuch-user-agent - 'notmuch-mua-mail - 'notmuch-mua-send-and-exit - 'notmuch-mua-kill-buffer - 'notmuch-mua-send-hook) - -;; Add some more headers to the list that `message-mode' hides when -;; composing a message. -(notmuch-mua-add-more-hidden-headers) - -(provide 'notmuch-mua) - -;;; notmuch-mua.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-mua.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-mua.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-parser.el b/emacs/elpa/notmuch-20231006.2337/notmuch-parser.el @@ -1,194 +0,0 @@ -;;; notmuch-parser.el --- streaming S-expression parser -*- lexical-binding: t -*- -;; -;; Copyright © Austin Clements -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: Austin Clements <aclements@csail.mit.edu> - -;;; Code: - -(require 'cl-lib) -(require 'pcase) -(require 'subr-x) - -(defun notmuch-sexp-create-parser () - "Return a new streaming S-expression parser. - -This parser is designed to incrementally read an S-expression -whose structure is known to the caller. Like a typical -S-expression parsing interface, it provides a function to read a -complete S-expression from the input. However, it extends this -with an additional function that requires the next value in the -input to be a list and descends into it, allowing its elements to -be read one at a time or further descended into. Both functions -can return \\='retry to indicate that not enough input is available. - -The parser always consumes input from point in the current -buffer. Hence, the caller is allowed to delete any data before -point and may resynchronize after an error by moving point." - (vector 'notmuch-sexp-parser - 0 ; List depth - nil ; Partial parse position marker - nil)) ; Partial parse state - -(defmacro notmuch-sexp--depth (sp) `(aref ,sp 1)) -(defmacro notmuch-sexp--partial-pos (sp) `(aref ,sp 2)) -(defmacro notmuch-sexp--partial-state (sp) `(aref ,sp 3)) - -(defun notmuch-sexp-read (sp) - "Consume and return the value at point in the current buffer. - -Returns \\='retry if there is insufficient input to parse a complete -value (though it may still move point over whitespace). If the -parser is currently inside a list and the next token ends the -list, this moves point just past the terminator and returns \\='end. -Otherwise, this moves point to just past the end of the value and -returns the value." - (skip-chars-forward " \n\r\t") - (cond ((eobp) 'retry) - ((= (char-after) ?\)) - ;; We've reached the end of a list - (if (= (notmuch-sexp--depth sp) 0) - ;; .. but we weren't in a list. Let read signal the - ;; error to be consistent with all other code paths. - (read (current-buffer)) - ;; Go up a level and return an end token - (cl-decf (notmuch-sexp--depth sp)) - (forward-char) - 'end)) - ((= (char-after) ?\() - ;; We're at the beginning of a list. If we haven't started - ;; a partial parse yet, attempt to read the list in its - ;; entirety. If this fails, or we've started a partial - ;; parse, extend the partial parse to figure out when we - ;; have a complete list. - (catch 'return - (unless (notmuch-sexp--partial-state sp) - (let ((start (point))) - (condition-case nil - (throw 'return (read (current-buffer))) - (end-of-file (goto-char start))))) - ;; Extend the partial parse - (let (is-complete) - (save-excursion - (let* ((new-state (parse-partial-sexp - (or (notmuch-sexp--partial-pos sp) (point)) - (point-max) 0 nil - (notmuch-sexp--partial-state sp))) - ;; A complete value is available if we've - ;; reached depth 0. - (depth (car new-state))) - (cl-assert (>= depth 0)) - (if (= depth 0) - ;; Reset partial parse state - (setf (notmuch-sexp--partial-state sp) nil - (notmuch-sexp--partial-pos sp) nil - is-complete t) - ;; Update partial parse state - (setf (notmuch-sexp--partial-state sp) new-state - (notmuch-sexp--partial-pos sp) (point-marker))))) - (if is-complete - (read (current-buffer)) - 'retry)))) - (t - ;; Attempt to read a non-compound value - (let ((start (point))) - (condition-case nil - (let ((val (read (current-buffer)))) - ;; We got what looks like a complete read, but if - ;; we reached the end of the buffer in the process, - ;; we may not actually have all of the input we - ;; need (unless it's a string, which is delimited). - (if (or (stringp val) (not (eobp))) - val - ;; We can't be sure the input was complete - (goto-char start) - 'retry)) - (end-of-file - (goto-char start) - 'retry)))))) - -(defun notmuch-sexp-begin-list (sp) - "Parse the beginning of a list value and enter the list. - -Returns \\='retry if there is insufficient input to parse the -beginning of the list. If this is able to parse the beginning of -a list, it moves point past the token that opens the list and -returns t. Later calls to `notmuch-sexp-read' will return the -elements inside the list. If the input in buffer is not the -beginning of a list, throw invalid-read-syntax." - (skip-chars-forward " \n\r\t") - (cond ((eobp) 'retry) - ((= (char-after) ?\() - (forward-char) - (cl-incf (notmuch-sexp--depth sp)) - t) - (t - ;; Skip over the bad character like `read' does - (forward-char) - (signal 'invalid-read-syntax (list (string (char-before))))))) - -(defvar notmuch-sexp--parser nil - "The buffer-local notmuch-sexp-parser instance. - -Used by `notmuch-sexp-parse-partial-list'.") - -(defvar notmuch-sexp--state nil - "The buffer-local `notmuch-sexp-parse-partial-list' state.") - -(defun notmuch-sexp-parse-partial-list (result-function result-buffer) - "Incrementally parse an S-expression list from the current buffer. - -This function consumes an S-expression list from the current -buffer, applying RESULT-FUNCTION in RESULT-BUFFER to each -complete value in the list. It operates incrementally and should -be called whenever the input buffer has been extended with -additional data. The caller just needs to ensure it does not -move point in the input buffer." - ;; Set up the initial state - (unless (local-variable-p 'notmuch-sexp--parser) - (setq-local notmuch-sexp--parser (notmuch-sexp-create-parser)) - (setq-local notmuch-sexp--state 'begin)) - (let (done) - (while (not done) - (cl-case notmuch-sexp--state - (begin - ;; Enter the list - (if (eq (notmuch-sexp-begin-list notmuch-sexp--parser) 'retry) - (setq done t) - (setq notmuch-sexp--state 'result))) - (result - ;; Parse a result - (let ((result (notmuch-sexp-read notmuch-sexp--parser))) - (cl-case result - (retry (setq done t)) - (end (setq notmuch-sexp--state 'end)) - (t (with-current-buffer result-buffer - (funcall result-function result)))))) - (end - ;; Skip over trailing whitespace. - (skip-chars-forward " \n\r\t") - ;; Any trailing data is unexpected. - (unless (eobp) - (error "Trailing garbage following expression")) - (setq done t))))) - ;; Clear out what we've parsed - (delete-region (point-min) (point))) - -(provide 'notmuch-parser) - -;;; notmuch-parser.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-parser.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-parser.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-pkg.el b/emacs/elpa/notmuch-20231006.2337/notmuch-pkg.el @@ -1,4 +0,0 @@ -(define-package "notmuch" "20231006.2337" "run notmuch within emacs" 'nil :commit "e4ead7656c72092bf30c43283057c2d4c4107962" :url "https://notmuchmail.org") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-print.el b/emacs/elpa/notmuch-20231006.2337/notmuch-print.el @@ -1,100 +0,0 @@ -;;; notmuch-print.el --- printing messages from notmuch -*- lexical-binding: t -*- -;; -;; Copyright © David Edmondson -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: David Edmondson <dme@dme.org> - -;;; Code: - -(require 'notmuch-lib) - -(declare-function notmuch-show-get-prop "notmuch-show" (prop &optional props)) - -;;; Options - -(defcustom notmuch-print-mechanism 'notmuch-print-lpr - "How should printing be done?" - :group 'notmuch-show - :type '(choice - (function :tag "Use lpr" notmuch-print-lpr) - (function :tag "Use ps-print" notmuch-print-ps-print) - (function :tag "Use ps-print then evince" notmuch-print-ps-print/evince) - (function :tag "Use muttprint" notmuch-print-muttprint) - (function :tag "Use muttprint then evince" notmuch-print-muttprint/evince) - (function :tag "Using a custom function"))) - -;;; Utility functions - -(defun notmuch-print-run-evince (file) - "View FILE using `evince'." - (start-process "evince" nil "evince" file)) - -(defun notmuch-print-run-muttprint (&optional output) - "Pass the contents of the current buffer to `muttprint'. - -Optional OUTPUT allows passing a list of flags to muttprint." - (apply #'notmuch--call-process-region (point-min) (point-max) - ;; Reads from stdin. - "muttprint" - nil nil nil - ;; Show the tags. - "--printed-headers" "Date_To_From_CC_Newsgroups_*Subject*_/Tags/" - output)) - -;;; User-visible functions - -(defun notmuch-print-lpr (_msg) - "Print a message buffer using lpr." - (lpr-buffer)) - -(defun notmuch-print-ps-print (msg) - "Print a message buffer using the ps-print package." - (let ((subject (notmuch-prettify-subject - (plist-get (notmuch-show-get-prop :headers msg) :Subject)))) - (rename-buffer subject t) - (ps-print-buffer))) - -(defun notmuch-print-ps-print/evince (msg) - "Preview a message buffer using ps-print and evince." - (let ((ps-file (make-temp-file "notmuch" nil ".ps")) - (subject (notmuch-prettify-subject - (plist-get (notmuch-show-get-prop :headers msg) :Subject)))) - (rename-buffer subject t) - (ps-print-buffer ps-file) - (notmuch-print-run-evince ps-file))) - -(defun notmuch-print-muttprint (_msg) - "Print a message using muttprint." - (notmuch-print-run-muttprint)) - -(defun notmuch-print-muttprint/evince (_msg) - "Preview a message buffer using muttprint and evince." - (let ((ps-file (make-temp-file "notmuch" nil ".ps"))) - (notmuch-print-run-muttprint (list "--printer" (concat "TO_FILE:" ps-file))) - (notmuch-print-run-evince ps-file))) - -(defun notmuch-print-message (msg) - "Print a message using the user-selected mechanism." - (set-buffer-modified-p nil) - (funcall notmuch-print-mechanism msg)) - -;;; _ - -(provide 'notmuch-print) - -;;; notmuch-print.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-print.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-print.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-query.el b/emacs/elpa/notmuch-20231006.2337/notmuch-query.el @@ -1,74 +0,0 @@ -;;; notmuch-query.el --- provide an emacs api to query notmuch -*- lexical-binding: t -*- -;; -;; Copyright © David Bremner -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: David Bremner <david@tethera.net> - -;;; Code: - -(require 'notmuch-lib) - -;;; Basic query function - -(define-obsolete-function-alias - 'notmuch-query-get-threads - #'notmuch--run-show - "notmuch 0.37") - -;;; Mapping functions across collections of messages - -(defun notmuch-query-map-aux (mapper function seq) - "Private function to do the actual mapping and flattening." - (cl-mapcan (lambda (tree) - (funcall mapper function tree)) - seq)) - -(defun notmuch-query-map-threads (fn threads) - "Apply function FN to every thread in THREADS. -Flatten results to a list. See the function -`notmuch-query-get-threads' for more information." - (notmuch-query-map-aux 'notmuch-query-map-forest fn threads)) - -(defun notmuch-query-map-forest (fn forest) - "Apply function FN to every message in FOREST. -Flatten results to a list. See the function -`notmuch-query-get-threads' for more information." - (notmuch-query-map-aux 'notmuch-query-map-tree fn forest)) - -(defun notmuch-query-map-tree (fn tree) - "Apply function FN to every message in TREE. -Flatten results to a list. See the function -`notmuch--run-show' for more information." - (cons (funcall fn (car tree)) - (notmuch-query-map-forest fn (cadr tree)))) - -;;; Predefined queries - -(defun notmuch-query-get-message-ids (&rest search-terms) - "Return a list of message-ids of messages that match SEARCH-TERMS." - (notmuch-query-map-threads - (lambda (msg) (plist-get msg :id)) - (notmuch--run-show search-terms))) - -;;; Everything in this library is obsolete -(dolist (fun '(map-aux map-threads map-forest map-tree get-message-ids)) - (make-obsolete (intern (format "notmuch-query-%s" fun)) nil "notmuch 0.37")) - -(provide 'notmuch-query) - -;;; notmuch-query.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-query.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-query.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-show.el b/emacs/elpa/notmuch-20231006.2337/notmuch-show.el @@ -1,2737 +0,0 @@ -;;; notmuch-show.el --- displaying notmuch forests -*- lexical-binding: t -*- -;; -;; Copyright © Carl Worth -;; Copyright © David Edmondson -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: Carl Worth <cworth@cworth.org> -;; David Edmondson <dme@dme.org> - -;;; Code: - -(require 'mm-view) -(require 'message) -(require 'mm-decode) -(require 'mailcap) -(require 'icalendar) -(require 'goto-addr) - -(require 'notmuch-lib) -(require 'notmuch-tag) -(require 'notmuch-wash) -(require 'notmuch-mua) -(require 'notmuch-crypto) -(require 'notmuch-print) -(require 'notmuch-draft) - -(declare-function notmuch-call-notmuch-process "notmuch-lib" (&rest args)) -(declare-function notmuch-search-next-thread "notmuch" nil) -(declare-function notmuch-search-previous-thread "notmuch" nil) -(declare-function notmuch-search-show-thread "notmuch") -(declare-function notmuch-foreach-mime-part "notmuch" (function mm-handle)) -(declare-function notmuch-count-attachments "notmuch" (mm-handle)) -(declare-function notmuch-save-attachments "notmuch" (mm-handle &optional queryp)) -(declare-function notmuch-tree "notmuch-tree" - (&optional query query-context target buffer-name - open-target unthreaded parent-buffer)) -(declare-function notmuch-tree-get-message-properties "notmuch-tree" nil) -(declare-function notmuch-unthreaded "notmuch-tree" - (&optional query query-context target buffer-name - open-target)) -(declare-function notmuch-read-query "notmuch" (prompt)) -(declare-function notmuch-draft-resume "notmuch-draft" (id)) - -(defvar shr-blocked-images) -(defvar gnus-blocked-images) -(defvar shr-content-function) -(defvar w3m-ignored-image-url-regexp) - -;;; Options - -(defcustom notmuch-message-headers '("Subject" "To" "Cc" "Date") - "Headers that should be shown in a message, in this order. - -For an open message, all of these headers will be made visible -according to `notmuch-message-headers-visible' or can be toggled -with `notmuch-show-toggle-visibility-headers'. For a closed message, -only the first header in the list will be visible." - :type '(repeat string) - :group 'notmuch-show) - -(defcustom notmuch-message-headers-visible t - "Should the headers be visible by default? - -If this value is non-nil, then all of the headers defined in -`notmuch-message-headers' will be visible by default in the display -of each message. Otherwise, these headers will be hidden and -`notmuch-show-toggle-visibility-headers' can be used to make them -visible for any given message." - :type 'boolean - :group 'notmuch-show) - -(defcustom notmuch-show-header-line t - "Show a header line in notmuch show buffers. - -If t (the default), the header line will contain the current -message's subject. - -If a string, this value is interpreted as a format string to be -passed to `format-spec` with `%s` as the substitution variable -for the message's subject. E.g., to display the subject trimmed -to a maximum of 80 columns, you could use \"%>-80s\" as format. - -If you assign to this variable a function, it will be called with -the subject as argument, and the return value will be used as the -header line format. Since the function is called with the -message buffer as the current buffer, it is also possible to -access any other properties of the message, using for instance -notmuch-show functions such as -`notmuch-show-get-message-properties'. - -Finally, if this variable is set to nil, no header is -displayed." - :type '(choice (const :tag "No header" ni) - (const :tag "Subject" t) - (string :tag "Format") - (function :tag "Function")) - :group 'notmuch-show) - -(defcustom notmuch-show-depth-limit nil - "Depth beyond which message bodies are displayed lazily. - -If bound to an integer, any message with tree depth greater than -this will have its body display lazily, initially -inserting only a button. - -If this variable is set to nil (the default) no such lazy -insertion is done." - :type '(choice (const :tag "No limit" nil) - (number :tag "Limit" 10)) - :group 'notmuch-show) - -(defcustom notmuch-show-height-limit nil - "Height (from leaves) beyond which message bodies are displayed lazily. - -If bound to an integer, any message with height in the message -tree greater than this will have its body displayed lazily, -initially only a button. - -If this variable is set to nil (the default) no such lazy -display is done." - :type '(choice (const :tag "No limit" nil) - (number :tag "Limit" 10)) - :group 'notmuch-show) - -(defcustom notmuch-show-relative-dates t - "Display relative dates in the message summary line." - :type 'boolean - :group 'notmuch-show) - -(defvar notmuch-show-markup-headers-hook '(notmuch-show-colour-headers) - "A list of functions called to decorate the headers listed in -`notmuch-message-headers'.") - -(defcustom notmuch-show-hook '(notmuch-show-turn-on-visual-line-mode) - "Functions called after populating a `notmuch-show' buffer." - :type 'hook - :options '(notmuch-show-turn-on-visual-line-mode) - :group 'notmuch-show - :group 'notmuch-hooks) - -(defcustom notmuch-show-insert-text/plain-hook - '(notmuch-wash-wrap-long-lines - notmuch-wash-tidy-citations - notmuch-wash-elide-blank-lines - notmuch-wash-excerpt-citations) - "Functions used to improve the display of text/plain parts." - :type 'hook - :options '(notmuch-wash-convert-inline-patch-to-part - notmuch-wash-wrap-long-lines - notmuch-wash-tidy-citations - notmuch-wash-elide-blank-lines - notmuch-wash-excerpt-citations) - :group 'notmuch-show - :group 'notmuch-hooks) - -(defcustom notmuch-show-max-text-part-size 100000 - "Maximum size of a text part to be shown by default in characters. - -Set to 0 to show the part regardless of size." - :type 'integer - :group 'notmuch-show) - -;; Mostly useful for debugging. -(defcustom notmuch-show-all-multipart/alternative-parts nil - "Should all parts of multipart/alternative parts be shown?" - :type 'boolean - :group 'notmuch-show) - -(defcustom notmuch-show-indent-messages-width 1 - "Width of message indentation in threads. - -Messages are shown indented according to their depth in a thread. -This variable determines the width of this indentation measured -in number of blanks. Defaults to `1', choose `0' to disable -indentation." - :type 'integer - :group 'notmuch-show) - -(defcustom notmuch-show-indent-multipart nil - "Should the sub-parts of a multipart/* part be indented?" - ;; dme: Not sure which is a good default. - :type 'boolean - :group 'notmuch-show) - -(defcustom notmuch-show-part-button-default-action 'notmuch-show-save-part - "Default part header button action (on ENTER or mouse click)." - :group 'notmuch-show - :type '(choice (const :tag "Save part" - notmuch-show-save-part) - (const :tag "View part" - notmuch-show-view-part) - (const :tag "View interactively" - notmuch-show-interactively-view-part))) - -(defcustom notmuch-show-only-matching-messages nil - "Only matching messages are shown by default." - :type 'boolean - :group 'notmuch-show) - -;; By default, block all external images to prevent privacy leaks and -;; potential attacks. -(defcustom notmuch-show-text/html-blocked-images "." - "Remote images that have URLs matching this regexp will be blocked." - :type '(choice (const nil) regexp) - :group 'notmuch-show) - -;;; Variables - -(defvar-local notmuch-show-thread-id nil) - -(defvar-local notmuch-show-parent-buffer nil) - -(defvar-local notmuch-show-query-context nil) - -(defvar-local notmuch-show-process-crypto nil) - -(defvar-local notmuch-show-elide-non-matching-messages nil) - -(defvar-local notmuch-show-indent-content t) - -(defvar-local notmuch-show-single-message nil) - -(defvar notmuch-show-attachment-debug nil - "If t log stdout and stderr from attachment handlers. - -When set to nil (the default) stdout and stderr from attachment -handlers is discarded. When set to t the stdout and stderr from -each attachment handler is logged in buffers with names beginning -\" *notmuch-part*\".") - -;;; Options - -(defcustom notmuch-show-stash-mlarchive-link-alist - '(("MARC" . "https://marc.info/?i=") - ("Mail Archive, The" . "https://mid.mail-archive.com/") - ("Lore" . "https://lore.kernel.org/r/") - ("Notmuch" . "https://nmbug.notmuchmail.org/nmweb/show/") - ;; FIXME: can these services be searched by `Message-Id' ? - ;; ("MarkMail" . "http://markmail.org/") - ;; ("Nabble" . "http://nabble.com/") - ;; ("opensubscriber" . "http://opensubscriber.com/") - ) - "List of Mailing List Archives to use when stashing links. - -This list is used for generating a Mailing List Archive reference -URI with the current message's Message-Id in -`notmuch-show-stash-mlarchive-link'. - -If the cdr of the alist element is not a function, the cdr is -expected to contain a URI that is concatenated with the current -message's Message-Id to create a ML archive reference URI. - -If the cdr is a function, the function is called with the -Message-Id as the argument, and the function is expected to -return the ML archive reference URI." - :type '(alist :key-type (string :tag "Name") - :value-type (choice - (string :tag "URL") - (function :tag "Function returning the URL"))) - :group 'notmuch-show) - -(defcustom notmuch-show-stash-mlarchive-link-default "MARC" - "Default Mailing List Archive to use when stashing links. - -This is used when `notmuch-show-stash-mlarchive-link' isn't -provided with an MLA argument nor `completing-read' input." - :type `(choice - ,@(mapcar - (lambda (mla) - (list 'const :tag (car mla) :value (car mla))) - notmuch-show-stash-mlarchive-link-alist)) - :group 'notmuch-show) - -(defcustom notmuch-show-mark-read-tags '("-unread") - "List of tag changes to apply to a message when it is marked as read. - -Tags starting with \"+\" (or not starting with either \"+\" or -\"-\") in the list will be added, and tags starting with \"-\" -will be removed from the message being marked as read. - -For example, if you wanted to remove an \"unread\" tag and add a -\"read\" tag (which would make little sense), you would set: - (\"-unread\" \"+read\")" - :type '(repeat string) - :group 'notmuch-show) - -(defcustom notmuch-show-mark-read-function #'notmuch-show-seen-current-message - "Function to control which messages are marked read. - -The function should take two arguments START and END which will -be the start and end of the visible portion of the buffer and -should mark the appropriate messages read by applying -`notmuch-show-mark-read'. This function will be called after -every user interaction with notmuch." - :type 'function - :group 'notmuch-show) - -(defcustom notmuch-show-imenu-indent nil - "Should Imenu display messages indented. - -By default, Imenu (see Info node `(emacs) Imenu') in a -notmuch-show buffer displays all messages straight. This is -because the default Emacs frontend for Imenu makes it difficult -to select an Imenu entry with spaces in front. Other imenu -frontends such as counsel-imenu does not have this limitation. -In these cases, Imenu entries can be indented to reflect the -position of the message in the thread." - :type 'boolean - :group 'notmuch-show) - -;;; Utilities - -(defmacro with-current-notmuch-show-message (&rest body) - "Evaluate body with current buffer set to the text of current message." - `(save-excursion - (let ((id (notmuch-show-get-message-id))) - (let ((buf (generate-new-buffer (concat "*notmuch-msg-" id "*")))) - (with-current-buffer buf - (let ((coding-system-for-read 'no-conversion)) - (notmuch--call-process notmuch-command nil t nil "show" "--format=raw" id)) - ,@body) - (kill-buffer buf))))) - -(defun notmuch-show-turn-on-visual-line-mode () - "Enable Visual Line mode." - (visual-line-mode t)) - -;;; Commands - -;; DEPRECATED in Notmuch 0.16 since we now have convenient part -;; commands. We'll keep the command around for a version or two in -;; case people want to bind it themselves. -(defun notmuch-show-view-all-mime-parts () - "Use external viewers to view all attachments from the current message." - (interactive) - (with-current-notmuch-show-message - ;; We override the mm-inline-media-tests to indicate which message - ;; parts are already sufficiently handled by the original - ;; presentation of the message in notmuch-show mode. These parts - ;; will be inserted directly into the temporary buffer of - ;; with-current-notmuch-show-message and silently discarded. - ;; - ;; Any MIME part not explicitly mentioned here will be handled by an - ;; external viewer as configured in the various mailcap files. - (let ((mm-inline-media-tests - '(("text/.*" ignore identity) - ("application/pgp-signature" ignore identity) - ("multipart/alternative" ignore identity) - ("multipart/mixed" ignore identity) - ("multipart/related" ignore identity)))) - (mm-display-parts (mm-dissect-buffer))))) - -(defun notmuch-show-save-attachments () - "Save all attachments from the current message." - (interactive) - (with-current-notmuch-show-message - (let ((mm-handle (mm-dissect-buffer))) - (notmuch-save-attachments - mm-handle (> (notmuch-count-attachments mm-handle) 1)))) - (message "Done")) - -(defun notmuch-show-with-message-as-text (fn) - "Apply FN to a text representation of the current message. - -FN is called with one argument, the message properties. It should -operation on the contents of the current buffer." - ;; Remake the header to ensure that all information is available. - (let* ((to (notmuch-show-get-to)) - (cc (notmuch-show-get-cc)) - (from (notmuch-show-get-from)) - (subject (notmuch-show-get-subject)) - (date (notmuch-show-get-date)) - (tags (notmuch-show-get-tags)) - (depth (notmuch-show-get-depth)) - (header (concat - "Subject: " subject "\n" - "To: " to "\n" - (if (not (string-empty-p cc)) - (concat "Cc: " cc "\n") - "") - "From: " from "\n" - "Date: " date "\n" - (if tags - (concat "Tags: " - (mapconcat #'identity tags ", ") "\n") - ""))) - (all (buffer-substring (notmuch-show-message-top) - (notmuch-show-message-bottom))) - - (props (notmuch-show-get-message-properties)) - (indenting notmuch-show-indent-content)) - (with-temp-buffer - (insert all) - (when indenting - (indent-rigidly (point-min) - (point-max) - (- (* notmuch-show-indent-messages-width depth)))) - ;; Remove the original header. - (goto-char (point-min)) - (re-search-forward "^$" (point-max) nil) - (delete-region (point-min) (point)) - (insert header) - (funcall fn props)))) - -(defun notmuch-show-print-message () - "Print the current message." - (interactive) - (notmuch-show-with-message-as-text 'notmuch-print-message)) - -;;; Headers - -(defun notmuch-show-fontify-header () - (let ((face (cond - ((looking-at "[Tt]o:") - 'message-header-to) - ((looking-at "[Bb]?[Cc][Cc]:") - 'message-header-cc) - ((looking-at "[Ss]ubject:") - 'message-header-subject) - (t - 'message-header-other)))) - (overlay-put (make-overlay (point) (re-search-forward ":")) - 'face 'message-header-name) - (overlay-put (make-overlay (point) (re-search-forward ".*$")) - 'face face))) - -(defun notmuch-show-colour-headers () - "Apply some colouring to the current headers." - (goto-char (point-min)) - (while (looking-at "^[A-Za-z][-A-Za-z0-9]*:") - (notmuch-show-fontify-header) - (forward-line))) - -(defun notmuch-show-spaces-n (n) - "Return a string comprised of `n' spaces." - (make-string n ? )) - -(defun notmuch-show-update-tags (tags) - "Update the displayed tags of the current message." - (save-excursion - (let ((inhibit-read-only t) - (start (notmuch-show-message-top)) - (depth (notmuch-show-get-prop :depth)) - (orig-tags (notmuch-show-get-prop :orig-tags)) - (props (notmuch-show-get-message-properties)) - (extent (notmuch-show-message-extent))) - (goto-char start) - (notmuch-show-insert-headerline props depth tags orig-tags) - (put-text-property start (1+ start) - :notmuch-message-properties props) - (put-text-property (car extent) (cdr extent) :notmuch-message-extent extent) - ;; delete original headerline, but do not save to kill ring - (delete-region (point) (1+ (line-end-position)))))) - -(defun notmuch-clean-address (address) - "Try to clean a single email ADDRESS for display. Return a cons -cell of (AUTHOR_EMAIL AUTHOR_NAME). Return (ADDRESS nil) if -parsing fails." - (condition-case nil - (let (p-name p-address) - ;; It would be convenient to use `mail-header-parse-address', - ;; but that expects un-decoded mailbox parts, whereas our - ;; mailbox parts are already decoded (and hence may contain - ;; UTF-8). Given that notmuch should handle most of the awkward - ;; cases, some simple string deconstruction should be sufficient - ;; here. - (cond - ;; "User <user@dom.ain>" style. - ((string-match "\\(.*\\) <\\(.*\\)>" address) - (setq p-name (match-string 1 address)) - (setq p-address (match-string 2 address))) - - ;; "<user@dom.ain>" style. - ((string-match "<\\(.*\\)>" address) - (setq p-address (match-string 1 address))) - ;; Everything else. - (t - (setq p-address address))) - (when p-name - ;; Remove elements of the mailbox part that are not relevant for - ;; display, even if they are required during transport: - ;; - ;; Backslashes. - (setq p-name (replace-regexp-in-string "\\\\" "" p-name)) - ;; Outer single and double quotes, which might be nested. - (cl-loop with start-of-loop - do (setq start-of-loop p-name) - when (string-match "^\"\\(.*\\)\"$" p-name) - do (setq p-name (match-string 1 p-name)) - when (string-match "^'\\(.*\\)'$" p-name) - do (setq p-name (match-string 1 p-name)) - until (string= start-of-loop p-name))) - ;; If the address is 'foo@bar.com <foo@bar.com>' then show just - ;; 'foo@bar.com'. - (when (string= p-name p-address) - (setq p-name nil)) - (cons p-address p-name)) - (error (cons address nil)))) - -(defun notmuch-show-clean-address (address) - "Try to clean a single email ADDRESS for display. -Return unchanged ADDRESS if parsing fails." - (let* ((clean-address (notmuch-clean-address address)) - (p-address (car clean-address)) - (p-name (cdr clean-address))) - ;; If no name, return just the address. - (if (not p-name) - p-address - ;; Otherwise format the name and address together. - (concat p-name " <" p-address ">")))) - -(defun notmuch-show--mark-height (tree) - "Calculate and cache height (distance from deepest descendent)" - (let* ((msg (car tree)) - (children (cadr tree)) - (cached-height (plist-get msg :height))) - (or cached-height - (let ((height - (if (null children) 0 - (1+ (apply #'max (mapcar #'notmuch-show--mark-height children)))))) - (plist-put msg :height height) - height)))) - -(defun notmuch-show-insert-headerline (msg-plist depth tags &optional orig-tags) - "Insert a notmuch style headerline based on HEADERS for a -message at DEPTH in the current thread." - (let* ((start (point)) - (headers (plist-get msg-plist :headers)) - (duplicate (or (plist-get msg-plist :duplicate) 0)) - (file-count (length (plist-get msg-plist :filename))) - (date (or (and notmuch-show-relative-dates - (plist-get msg-plist :date_relative)) - (plist-get headers :Date))) - (from (notmuch-sanitize - (notmuch-show-clean-address (plist-get headers :From))))) - (when (string-match "\\cR" from) - ;; If the From header has a right-to-left character add - ;; invisible U+200E LEFT-TO-RIGHT MARK character which forces - ;; the header paragraph as left-to-right text. - (insert (propertize (string ?\x200e) 'invisible t))) - (insert (if notmuch-show-indent-content - (notmuch-show-spaces-n (* notmuch-show-indent-messages-width - depth)) - "") - from - " (" - date - ") (" - (notmuch-tag-format-tags tags (or orig-tags tags)) - ")") - (insert - (if (> file-count 1) - (let ((txt (format "%d/%d\n" duplicate file-count))) - (concat - (notmuch-show-spaces-n (max 0 (- (window-width) (+ (current-column) (length txt))))) - txt)) - "\n")) - (overlay-put (make-overlay start (point)) - 'face 'notmuch-message-summary-face))) - -(defun notmuch-show-insert-header (header header-value) - "Insert a single header." - (insert header ": " (notmuch-sanitize header-value) "\n")) - -(defun notmuch-show-insert-headers (headers) - "Insert the headers of the current message." - (let ((start (point))) - (mapc (lambda (header) - (let* ((header-symbol (intern (concat ":" header))) - (header-value (plist-get headers header-symbol))) - (when (and header-value - (not (string-equal "" header-value))) - (notmuch-show-insert-header header header-value)))) - notmuch-message-headers) - (save-excursion - (save-restriction - (narrow-to-region start (point-max)) - (run-hooks 'notmuch-show-markup-headers-hook))))) - -;;; Parts - -(define-button-type 'notmuch-show-part-button-type - 'action 'notmuch-show-part-button-default - 'follow-link t - 'face 'message-mml - :supertype 'notmuch-button-type) - -(defun notmuch-show-insert-part-header (_nth content-type declared-type - &optional name comment) - (let ((base-label (concat (and name (concat name ": ")) - declared-type - (and (not (string-equal declared-type content-type)) - (concat " (as " content-type ")")) - comment))) - (prog1 (insert-button - (concat "[ " base-label " ]") - :base-label base-label - :type 'notmuch-show-part-button-type - :notmuch-part-hidden nil) - (insert "\n")))) - -(defun notmuch-show-toggle-part-invisibility (&optional button) - (interactive) - (let ((button (or button (button-at (point))))) - (when button - (let ((overlay (button-get button 'overlay)) - (lazy-part (button-get button :notmuch-lazy-part))) - ;; We have a part to toggle if there is an overlay or if there - ;; is a lazy part. If neither is present we cannot toggle the - ;; part so we just return nil. - (when (or overlay lazy-part) - (let* ((show (button-get button :notmuch-part-hidden)) - (new-start (button-start button)) - (button-label (button-get button :base-label)) - (old-point (point)) - (properties (text-properties-at (button-start button))) - (inhibit-read-only t)) - ;; Toggle the button itself. - (button-put button :notmuch-part-hidden (not show)) - (goto-char new-start) - (insert "[ " button-label (if show " ]" " (hidden) ]")) - (set-text-properties new-start (point) properties) - (let ((old-end (button-end button))) - (move-overlay button new-start (point)) - (delete-region (point) old-end)) - (goto-char (min old-point (1- (button-end button)))) - ;; Return nil if there is a lazy-part, it is empty, and we are - ;; trying to show it. In all other cases return t. - (if lazy-part - (when show - (button-put button :notmuch-lazy-part nil) - (notmuch-show-lazy-part lazy-part button)) - (let* ((part (plist-get properties :notmuch-part)) - (undisplayer (plist-get part :undisplayer)) - (mime-type (plist-get part :computed-type)) - (redisplay-data (button-get button - :notmuch-redisplay-data)) - (imagep (string-match "^image/" mime-type))) - (cond - ((and imagep (not show) undisplayer) - ;; call undisplayer thunk created by gnus. - (funcall undisplayer) - ;; there is an extra newline left - (delete-region - (+ 1 (button-end button)) - (+ 2 (button-end button)))) - ((and imagep show redisplay-data) - (notmuch-show-lazy-part redisplay-data button)) - (t - (overlay-put overlay 'invisible (not show))))) - t))))))) - -;;; Part content ID handling - -(defvar notmuch-show--cids nil - "Alist from raw content ID to (MSG PART).") -(make-variable-buffer-local 'notmuch-show--cids) - -(defun notmuch-show--register-cids (msg part) - "Register content-IDs in PART and all of PART's sub-parts." - (let ((content-id (plist-get part :content-id))) - (when content-id - ;; Note that content-IDs are globally unique, except when they - ;; aren't: RFC 2046 section 5.1.4 permits children of a - ;; multipart/alternative to have the same content-ID, in which - ;; case the MUA is supposed to pick the best one it can render. - ;; We simply add the content-ID to the beginning of our alist; - ;; so if this happens, we'll take the last (and "best") - ;; alternative (even if we can't render it). - (push (list content-id msg part) notmuch-show--cids))) - ;; Recurse on sub-parts - (when-let ((type (plist-get part :content-type))) - (pcase-let ((`(,type ,subtype) - (split-string (downcase type) "/"))) - (cond ((equal type "multipart") - (mapc (apply-partially #'notmuch-show--register-cids msg) - (plist-get part :content))) - ((and (equal type "message") - (equal subtype "rfc822")) - (notmuch-show--register-cids - msg - (car (plist-get (car (plist-get part :content)) :body)))))))) - -(defun notmuch-show--get-cid-content (cid) - "Return a list (CID-content content-type) or nil. - -This will only find parts from messages that have been inserted -into the current buffer. CID must be a raw content ID, without -enclosing angle brackets, a cid: prefix, or URL encoding. This -will return nil if the CID is unknown or cannot be retrieved." - (when-let ((descriptor (cdr (assoc cid notmuch-show--cids)))) - (pcase-let ((`(,msg ,part) descriptor)) - ;; Request caching for this content, as some messages - ;; reference the same cid: part many times (hundreds!). - (list (notmuch-get-bodypart-binary - msg part notmuch-show-process-crypto 'cache) - (plist-get part :content-type))))) - -(defun notmuch-show-setup-w3m () - "Instruct w3m how to retrieve content from a \"related\" part of a message." - (interactive) - (when (and (boundp 'w3m-cid-retrieve-function-alist) - (not (assq 'notmuch-show-mode w3m-cid-retrieve-function-alist))) - (push (cons 'notmuch-show-mode #'notmuch-show--cid-w3m-retrieve) - w3m-cid-retrieve-function-alist)) - (setq mm-html-inhibit-images nil)) - -(defvar w3m-current-buffer) ;; From `w3m.el'. -(defun notmuch-show--cid-w3m-retrieve (url &rest _args) - ;; url includes the cid: prefix and is URL encoded (see RFC 2392). - (let* ((cid (url-unhex-string (substring url 4))) - (content-and-type - (with-current-buffer w3m-current-buffer - (notmuch-show--get-cid-content cid)))) - (when content-and-type - (insert (car content-and-type)) - (cadr content-and-type)))) - -;; MIME part renderers - -(defun notmuch-show-multipart/*-to-list (part) - (mapcar (lambda (inner-part) (plist-get inner-part :content-type)) - (plist-get part :content))) - -(defun notmuch-show-insert-part-multipart/alternative (msg part _content-type _nth depth _button) - (let ((chosen-type (car (notmuch-multipart/alternative-choose - msg (notmuch-show-multipart/*-to-list part)))) - (inner-parts (plist-get part :content)) - (start (point))) - ;; This inserts all parts of the chosen type rather than just one, - ;; but it's not clear that this is the wrong thing to do - which - ;; should be chosen if there are more than one that match? - (mapc (lambda (inner-part) - (let* ((inner-type (plist-get inner-part :content-type)) - (hide (not (or notmuch-show-all-multipart/alternative-parts - (string= chosen-type inner-type))))) - (notmuch-show-insert-bodypart msg inner-part depth hide))) - inner-parts) - - (when notmuch-show-indent-multipart - (indent-rigidly start (point) 1))) - t) - -(defun notmuch-show-insert-part-multipart/related (msg part _content-type _nth depth _button) - (let ((inner-parts (plist-get part :content)) - (start (point))) - ;; Render the primary part. FIXME: Support RFC 2387 Start header. - (notmuch-show-insert-bodypart msg (car inner-parts) depth) - ;; Add hidden buttons for the rest - (mapc (lambda (inner-part) - (notmuch-show-insert-bodypart msg inner-part depth t)) - (cdr inner-parts)) - (when notmuch-show-indent-multipart - (indent-rigidly start (point) 1))) - t) - -(defun notmuch-show-insert-part-multipart/signed (msg part _content-type _nth depth button) - (when button - (button-put button 'face 'notmuch-crypto-part-header)) - ;; Insert a button detailing the signature status. - (notmuch-crypto-insert-sigstatus-button (car (plist-get part :sigstatus)) - (notmuch-show-get-header :From msg)) - (let ((inner-parts (plist-get part :content)) - (start (point))) - ;; Show all of the parts. - (mapc (lambda (inner-part) - (notmuch-show-insert-bodypart msg inner-part depth)) - inner-parts) - (when notmuch-show-indent-multipart - (indent-rigidly start (point) 1))) - t) - -(defun notmuch-show-insert-part-multipart/encrypted (msg part _content-type _nth depth button) - (when button - (button-put button 'face 'notmuch-crypto-part-header)) - ;; Insert a button detailing the encryption status. - (notmuch-crypto-insert-encstatus-button (car (plist-get part :encstatus))) - ;; Insert a button detailing the signature status. - (notmuch-crypto-insert-sigstatus-button (car (plist-get part :sigstatus)) - (notmuch-show-get-header :From msg)) - (let ((inner-parts (plist-get part :content)) - (start (point))) - ;; Show all of the parts. - (mapc (lambda (inner-part) - (notmuch-show-insert-bodypart msg inner-part depth)) - inner-parts) - (when notmuch-show-indent-multipart - (indent-rigidly start (point) 1))) - t) - -(defun notmuch-show-insert-part-application/pgp-encrypted (_msg _part _content-type _nth _depth _button) - t) - -(defun notmuch-show-insert-part-multipart/* (msg part _content-type _nth depth _button) - (let ((inner-parts (plist-get part :content)) - (start (point))) - ;; Show all of the parts. - (mapc (lambda (inner-part) - (notmuch-show-insert-bodypart msg inner-part depth)) - inner-parts) - (when notmuch-show-indent-multipart - (indent-rigidly start (point) 1))) - t) - -(defun notmuch-show-insert-part-message/rfc822 (msg part _content-type _nth depth _button) - (let ((message (car (plist-get part :content)))) - (and - message - (let ((body (car (plist-get message :body))) - (start (point))) - ;; Override `notmuch-message-headers' to force `From' to be - ;; displayed. - (let ((notmuch-message-headers '("From" "Subject" "To" "Cc" "Date"))) - (notmuch-show-insert-headers (plist-get message :headers))) - ;; Blank line after headers to be compatible with the normal - ;; message display. - (insert "\n") - ;; Show the body - (notmuch-show-insert-bodypart msg body depth) - (when notmuch-show-indent-multipart - (indent-rigidly start (point) 1)) - t)))) - -(defun notmuch-show-insert-part-text/plain (msg part _content-type _nth depth button) - ;; For backward compatibility we want to apply the text/plain hook - ;; to the whole of the part including the part button if there is - ;; one. - (let ((start (if button - (button-start button) - (point)))) - (insert (notmuch-get-bodypart-text msg part notmuch-show-process-crypto)) - (save-excursion - (save-restriction - (narrow-to-region start (point-max)) - (run-hook-with-args 'notmuch-show-insert-text/plain-hook msg depth)))) - t) - -(defun notmuch-show-insert-part-text/calendar (msg part _content-type _nth _depth _button) - (insert (with-temp-buffer - (insert (notmuch-get-bodypart-text msg part notmuch-show-process-crypto)) - ;; notmuch-get-bodypart-text does no newline conversion. - ;; Replace CRLF with LF before icalendar can use it. - (goto-char (point-min)) - (while (re-search-forward "\r\n" nil t) - (replace-match "\n" nil nil)) - (let ((file (make-temp-file "notmuch-ical")) - result) - (unwind-protect - (progn - (unless (icalendar-import-buffer file t) - (error "Icalendar import error. %s" - "See *icalendar-errors* for more information")) - (set-buffer (get-file-buffer file)) - (setq result (buffer-substring (point-min) (point-max))) - (set-buffer-modified-p nil) - (kill-buffer (current-buffer))) - (delete-file file)) - result))) - t) - -;; For backwards compatibility. -(defun notmuch-show-insert-part-text/x-vcalendar (msg part _content-type _nth depth _button) - (notmuch-show-insert-part-text/calendar msg part nil nil depth nil)) - -(when (version< emacs-version "25.3") - ;; https://bugs.gnu.org/28350 - ;; - ;; For newer emacs, we fall back to notmuch-show-insert-part-*/* - ;; (see notmuch-show-handlers-for) - (defun notmuch-show-insert-part-text/enriched - (msg part content-type nth depth button) - ;; By requiring enriched below, we ensure that the function - ;; enriched-decode-display-prop is defined before it will be - ;; shadowed by the letf below. Otherwise the version in - ;; enriched.el may be loaded a bit later and used instead (for - ;; the first time). - (require 'enriched) - (cl-letf (((symbol-function 'enriched-decode-display-prop) - (lambda (start end &optional _param) (list start end)))) - (notmuch-show-insert-part-*/* msg part content-type nth depth button)))) - -(defun notmuch-show-get-mime-type-of-application/octet-stream (part) - ;; If we can deduce a MIME type from the filename of the attachment, - ;; we return that. - (and (plist-get part :filename) - (let ((extension (file-name-extension (plist-get part :filename)))) - (and extension - (progn - (mailcap-parse-mimetypes) - (let ((mime-type (mailcap-extension-to-mime extension))) - (and mime-type - (not (string-equal mime-type "application/octet-stream")) - mime-type))))))) - -(defun notmuch-show-insert-part-text/html (msg part content-type nth depth button) - (if (eq mm-text-html-renderer 'shr) - ;; It's easier to drive shr ourselves than to work around the - ;; goofy things `mm-shr' does (like irreversibly taking over - ;; content ID handling). - ;; FIXME: If we block an image, offer a button to load external - ;; images. - (let ((shr-blocked-images notmuch-show-text/html-blocked-images)) - (notmuch-show--insert-part-text/html-shr msg part)) - ;; Otherwise, let message-mode do the heavy lifting - ;; - ;; w3m sets up a keymap which "leaks" outside the invisible region - ;; and causes strange effects in notmuch. We set - ;; mm-inline-text-html-with-w3m-keymap to nil to tell w3m not to - ;; set a keymap (so the normal notmuch-show-mode-map remains). - (let ((mm-inline-text-html-with-w3m-keymap nil) - ;; FIXME: If we block an image, offer a button to load external - ;; images. - (gnus-blocked-images notmuch-show-text/html-blocked-images) - (w3m-ignored-image-url-regexp notmuch-show-text/html-blocked-images)) - (notmuch-show-insert-part-*/* msg part content-type nth depth button)))) - -;;; Functions used by notmuch-show--insert-part-text/html-shr - -(declare-function libxml-parse-html-region "xml.c") -(declare-function shr-insert-document "shr") - -(defun notmuch-show--insert-part-text/html-shr (msg part) - ;; Make sure shr is loaded before we start let-binding its globals - (require 'shr) - (let ((dom (let ((process-crypto notmuch-show-process-crypto)) - (with-temp-buffer - (insert (notmuch-get-bodypart-text msg part process-crypto)) - (libxml-parse-html-region (point-min) (point-max))))) - (shr-content-function - (lambda (url) - ;; shr strips the "cid:" part of URL, but doesn't - ;; URL-decode it (see RFC 2392). - (let ((cid (url-unhex-string url))) - (car (notmuch-show--get-cid-content cid)))))) - (shr-insert-document dom) - t)) - -(defun notmuch-show-insert-part-*/* (msg part content-type _nth _depth _button) - ;; This handler _must_ succeed - it is the handler of last resort. - (notmuch-mm-display-part-inline msg part content-type notmuch-show-process-crypto) - t) - -;;; Functions for determining how to handle MIME parts. - -(defun notmuch-show-handlers-for (content-type) - "Return a list of content handlers for a part of type CONTENT-TYPE." - (let (result) - (mapc (lambda (func) - (when (functionp func) - (push func result))) - ;; Reverse order of prefrence. - (list (intern (concat "notmuch-show-insert-part-*/*")) - (intern (concat "notmuch-show-insert-part-" - (car (split-string content-type "/")) - "/*")) - (intern (concat "notmuch-show-insert-part-" content-type)))) - result)) - -;;; Parts - -(defun notmuch-show-insert-bodypart-internal (msg part content-type nth depth button) - ;; Run the handlers until one of them succeeds. - (cl-loop for handler in (notmuch-show-handlers-for content-type) - until (condition-case err - (funcall handler msg part content-type nth depth button) - ;; Specifying `debug' here lets the debugger run if - ;; `debug-on-error' is non-nil. - ((debug error) - (insert "!!! Bodypart handler `" (prin1-to-string handler) - "' threw an error:\n" - "!!! " (error-message-string err) "\n") - nil)))) - -(defun notmuch-show-create-part-overlays (button beg end) - "Add an overlay to the part between BEG and END." - ;; If there is no button (i.e., the part is text/plain and the first - ;; part) or if the part has no content then we don't make the part - ;; toggleable. - (when (and button (/= beg end)) - (button-put button 'overlay (make-overlay beg end)) - ;; Return true if we created an overlay. - t)) - -(defun notmuch-show-record-part-information (part beg end) - "Store PART as a text property from BEG to END." - ;; Record part information. Since we already inserted subparts, - ;; don't override existing :notmuch-part properties. - (notmuch-map-text-property beg end :notmuch-part - (lambda (v) (or v part))) - ;; Make :notmuch-part front sticky and rear non-sticky so it stays - ;; applied to the beginning of each line when we indent the - ;; message. Since we're operating on arbitrary renderer output, - ;; watch out for sticky specs of t, which means all properties are - ;; front-sticky/rear-nonsticky. - (notmuch-map-text-property beg end 'front-sticky - (lambda (v) - (if (listp v) - (cl-pushnew :notmuch-part v) - v))) - (notmuch-map-text-property beg end 'rear-nonsticky - (lambda (v) - (if (listp v) - (cl-pushnew :notmuch-part v) - v)))) - -(defun notmuch-show-lazy-part (part-args button) - ;; Insert the lazy part after the button for the part. We would just - ;; move to the start of the new line following the button and insert - ;; the part but that point might have text properties (eg colours - ;; from a message header etc) so instead we start from the last - ;; character of the button by adding a newline and finish by - ;; removing the extra newline from the end of the part. - (save-excursion - (goto-char (button-end button)) - (insert "\n") - (let* ((inhibit-read-only t) - ;; We need to use markers for the start and end of the part - ;; because the part insertion functions do not guarantee - ;; to leave point at the end of the part. - (part-beg (copy-marker (point) nil)) - (part-end (copy-marker (point) t)) - ;; We have to save the depth as we can't find the depth - ;; when narrowed. - (depth (notmuch-show-get-depth)) - (mime-type (plist-get (cadr part-args) :computed-type))) - (save-restriction - (narrow-to-region part-beg part-end) - (delete-region part-beg part-end) - (when (and mime-type (string-match "^image/" mime-type)) - (button-put button :notmuch-redisplay-data part-args)) - (apply #'notmuch-show-insert-bodypart-internal part-args) - (indent-rigidly part-beg - part-end - (* notmuch-show-indent-messages-width depth))) - (goto-char part-end) - (delete-char 1) - (notmuch-show-record-part-information (cadr part-args) - (button-start button) - part-end) - ;; Create the overlay. If the lazy-part turned out to be empty/not - ;; showable this returns nil. - (notmuch-show-create-part-overlays button part-beg part-end)))) - -(defun notmuch-show-mime-type (part) - "Return the correct mime-type to use for PART." - (when-let ((content-type (plist-get part :content-type))) - (setq content-type (downcase content-type)) - (or (and (string= content-type "application/octet-stream") - (notmuch-show-get-mime-type-of-application/octet-stream part)) - (and (string= content-type "inline patch") - "text/x-diff") - content-type))) - -;; The following variable can be overridden by let bindings. -(defvar notmuch-show-insert-header-p-function 'notmuch-show-insert-header-p - "Specify which function decides which part headers get inserted. - -The function should take two parameters, PART and HIDE, and -should return non-NIL if a header button should be inserted for -this part.") - -(defun notmuch-show-insert-header-p (part _hide) - ;; Show all part buttons except for the first part if it is text/plain. - (let ((mime-type (notmuch-show-mime-type part))) - (not (and (string= mime-type "text/plain") - (<= (plist-get part :id) 1))))) - -(defun notmuch-show-reply-insert-header-p-never (_part _hide) - nil) - -(defun notmuch-show-reply-insert-header-p-trimmed (part hide) - (let ((mime-type (notmuch-show-mime-type part))) - (and (not (notmuch-match-content-type mime-type "multipart/*")) - (not hide)))) - -(defun notmuch-show-reply-insert-header-p-minimal (part hide) - (let ((mime-type (notmuch-show-mime-type part))) - (and (notmuch-match-content-type mime-type "text/*") - (not hide)))) - -(defun notmuch-show-insert-bodypart (msg part depth &optional hide) - "Insert the body part PART at depth DEPTH in the current thread. - -HIDE determines whether to show or hide the part and the button -as follows: If HIDE is nil, show the part and the button. If HIDE -is t, hide the part initially and show the button." - (let* ((content-type (plist-get part :content-type)) - (mime-type (notmuch-show-mime-type part)) - (nth (plist-get part :id)) - (height (plist-get msg :height)) - (long (and (notmuch-match-content-type mime-type "text/*") - (> notmuch-show-max-text-part-size 0) - (> (length (plist-get part :content)) - notmuch-show-max-text-part-size))) - (deep (and notmuch-show-depth-limit - (> depth notmuch-show-depth-limit))) - (high (and notmuch-show-height-limit - (> height notmuch-show-height-limit))) - (beg (point)) - ;; This default header-p function omits the part button for - ;; the first (or only) part if this is text/plain. - (button (and (or deep long high - (funcall notmuch-show-insert-header-p-function part hide)) - (notmuch-show-insert-part-header - nth mime-type - (and content-type (downcase content-type)) - (plist-get part :filename)))) - ;; Hide the part initially if HIDE is t, or if it is too long/deep - ;; and we have a button to allow toggling. - (show-part (not (or (equal hide t) - (and deep button) - (and high button) - (and long button)))) - (content-beg (point)) - (part-data (list msg part mime-type nth depth button))) - ;; Store the computed mime-type for later use (e.g. by attachment handlers). - (plist-put part :computed-type mime-type) - (cond - (show-part - (apply #'notmuch-show-insert-bodypart-internal part-data) - (when (and button (string-match "^image/" mime-type)) - (button-put button :notmuch-redisplay-data part-data))) - (t - (when button - (button-put button :notmuch-lazy-part part-data)))) - ;; Some of the body part handlers leave point somewhere up in the - ;; part, so we make sure that we're down at the end. - (goto-char (point-max)) - ;; Ensure that the part ends with a carriage return. - (unless (bolp) - (insert "\n")) - ;; We do not create the overlay for hidden (lazy) parts until - ;; they are inserted. - (if show-part - (notmuch-show-create-part-overlays button content-beg (point)) - (save-excursion - (notmuch-show-toggle-part-invisibility button))) - (notmuch-show-record-part-information part beg (point)))) - -(defun notmuch-show-insert-body (msg body depth) - "Insert the body BODY at depth DEPTH in the current thread." - ;; Register all content IDs for this message. According to RFC - ;; 2392, content IDs are *global*, but it's okay if an MUA treats - ;; them as only global within a message. - (notmuch-show--register-cids msg (car body)) - (mapc (lambda (part) (notmuch-show-insert-bodypart msg part depth)) body)) - -(defun notmuch-show-make-symbol (type) - (make-symbol (concat "notmuch-show-" type))) - -(defun notmuch-show-strip-re (string) - (replace-regexp-in-string "^\\([Rr]e: *\\)+" "" string)) - -(defvar notmuch-show-previous-subject "") -(make-variable-buffer-local 'notmuch-show-previous-subject) - -(defun notmuch-show-choose-duplicate (duplicate) - "Display message file with index DUPLICATE in place of the current one. - -Message file indices are based on the order the files are -discovered by `notmuch new' (and hence are somewhat arbitrary), -and correspond to those passed to the \"\\-\\-duplicate\" arguments -to the CLI. - -When called interactively, the function will prompt for the index -of the file to display. An error will be signaled if the index -is out of range." - (interactive "Nduplicate: ") - (let ((count (length (notmuch-show-get-prop :filename)))) - (when (or (> duplicate count) - (< duplicate 1)) - (error "Duplicate %d out of range [1,%d]" duplicate count))) - (notmuch-show-move-to-message-top) - (save-excursion - (let* ((extent (notmuch-show-message-extent)) - (id (notmuch-show-get-message-id)) - (depth (notmuch-show-get-depth)) - (inhibit-read-only t) - (new-msg (notmuch--run-show (list id) duplicate))) - ;; clean up existing overlays to avoid extending them. - (dolist (o (overlays-in (car extent) (cdr extent))) - (delete-overlay o)) - ;; pretend insertion is happening at end of buffer - (narrow-to-region (point-min) (car extent)) - ;; Insert first, then delete, to avoid marker for start of next - ;; message being in same place as the start of this one. - (notmuch-show-insert-msg new-msg depth) - (widen) - (delete-region (point) (cdr extent))))) - -(defun notmuch-show-insert-msg (msg depth) - "Insert the message MSG at depth DEPTH in the current thread." - (let* ((headers (plist-get msg :headers)) - ;; Indentation causes the buffer offset of the start/end - ;; points to move, so we must use markers. - message-start message-end - content-start content-end - headers-start headers-end - (bare-subject (notmuch-show-strip-re (plist-get headers :Subject)))) - (setq message-start (point-marker)) - (notmuch-show-insert-headerline msg depth (plist-get msg :tags)) - (setq content-start (point-marker)) - ;; Set `headers-start' to point after the 'Subject:' header to be - ;; compatible with the existing implementation. This just sets it - ;; to after the first header. - (notmuch-show-insert-headers headers) - (save-excursion - (goto-char content-start) - ;; If the subject of this message is the same as that of the - ;; previous message, don't display it when this message is - ;; collapsed. - (unless (string= notmuch-show-previous-subject bare-subject) - (forward-line 1)) - (setq headers-start (point-marker))) - (setq headers-end (point-marker)) - (setq notmuch-show-previous-subject bare-subject) - ;; A blank line between the headers and the body. - (insert "\n") - (notmuch-show-insert-body msg (plist-get msg :body) - (if notmuch-show-indent-content depth 0)) - ;; Ensure that the body ends with a newline. - (unless (bolp) - (insert "\n")) - (setq content-end (point-marker)) - ;; Indent according to the depth in the thread. - (when notmuch-show-indent-content - (indent-rigidly content-start - content-end - (* notmuch-show-indent-messages-width depth))) - (setq message-end (point-max-marker)) - ;; Save the extents of this message over the whole text of the - ;; message. - (put-text-property message-start message-end - :notmuch-message-extent - (cons message-start message-end)) - ;; Create overlays used to control visibility - (plist-put msg :headers-overlay (make-overlay headers-start headers-end)) - (plist-put msg :message-overlay (make-overlay headers-start content-end)) - (plist-put msg :depth depth) - ;; Save the properties for this message. Currently this saves the - ;; entire message (augmented it with other stuff), which seems - ;; like overkill. We might save a reduced subset (for example, not - ;; the content). - (notmuch-show-set-message-properties msg) - ;; Set header visibility. - (notmuch-show-headers-visible msg notmuch-message-headers-visible) - ;; Message visibility depends on whether it matched the search - ;; criteria. - (notmuch-show-message-visible msg (and (plist-get msg :match) - (not (plist-get msg :excluded)))))) - -;;; Toggle commands - -(defun notmuch-show-toggle-process-crypto () - "Toggle the processing of cryptographic MIME parts." - (interactive) - (setq notmuch-show-process-crypto (not notmuch-show-process-crypto)) - (message (if notmuch-show-process-crypto - "Processing cryptographic MIME parts." - "Not processing cryptographic MIME parts.")) - (notmuch-show-refresh-view)) - -(defun notmuch-show-toggle-elide-non-matching () - "Toggle the display of non-matching messages." - (interactive) - (setq notmuch-show-elide-non-matching-messages - (not notmuch-show-elide-non-matching-messages)) - (message (if notmuch-show-elide-non-matching-messages - "Showing matching messages only." - "Showing all messages.")) - (notmuch-show-refresh-view)) - -(defun notmuch-show-toggle-thread-indentation () - "Toggle the indentation of threads." - (interactive) - (setq notmuch-show-indent-content (not notmuch-show-indent-content)) - (message (if notmuch-show-indent-content - "Content is indented." - "Content is not indented.")) - (notmuch-show-refresh-view)) - -;;; Main insert functions - -(defun notmuch-show-insert-tree (tree depth) - "Insert the message tree TREE at depth DEPTH in the current thread." - (let ((msg (car tree)) - (replies (cadr tree))) - ;; We test whether there is a message or just some replies. - (when msg - (notmuch-show--mark-height tree) - (notmuch-show-insert-msg msg depth)) - (notmuch-show-insert-thread replies (1+ depth)))) - -(defun notmuch-show-insert-thread (thread depth) - "Insert the thread THREAD at depth DEPTH in the current forest." - (mapc (lambda (tree) (notmuch-show-insert-tree tree depth)) thread)) - -(defun notmuch-show-insert-forest (forest) - "Insert the forest of threads FOREST." - (mapc (lambda (thread) (notmuch-show-insert-thread thread 0)) forest)) - -;;; Link buttons - -(defvar notmuch-id-regexp - (concat - ;; Match the id: prefix only if it begins a word (to disallow, for - ;; example, matching cid:). - "\\<id:\\(" - ;; If the term starts with a ", then parse Xapian's quoted boolean - ;; term syntax, which allows for anything as long as embedded - ;; double quotes escaped by doubling them. We also disallow - ;; newlines (which Xapian allows) to prevent runaway terms. - "\"\\([^\"\n]\\|\"\"\\)*\"" - ;; Otherwise, parse Xapian's unquoted syntax, which goes up to the - ;; next space or ). We disallow [.,;] as the last character - ;; because these are probably part of the surrounding text, and not - ;; part of the id. This doesn't match single character ids; meh. - "\\|[^\"[:space:])][^[:space:])]*[^])[:space:].,:;?!]" - "\\)") - "The regexp used to match id: links in messages.") - -(defvar notmuch-mid-regexp - ;; goto-address-url-regexp matched cid: links, which have the same - ;; grammar as the message ID part of a mid: link. Construct the - ;; regexp using the same technique as goto-address-url-regexp. - (concat "\\<mid:\\(" thing-at-point-url-path-regexp "\\)") - "The regexp used to match mid: links in messages. - -See RFC 2392.") - -(defun notmuch-show-buttonise-links (start end) - "Buttonise URLs and mail addresses between START and END. - -This also turns id:\"<message id>\"-parts and mid: links into -buttons for a corresponding notmuch search." - (goto-address-fontify-region start end) - (save-excursion - (let (links - (beg-line (progn (goto-char start) (line-beginning-position))) - (end-line (progn (goto-char end) (line-end-position)))) - (goto-char beg-line) - (while (re-search-forward notmuch-id-regexp end-line t) - (push (list (match-beginning 0) (match-end 0) - (match-string-no-properties 0)) links)) - (goto-char beg-line) - (while (re-search-forward notmuch-mid-regexp end-line t) - (let* ((mid-cid (match-string-no-properties 1)) - (mid (save-match-data - (string-match "^[^/]*" mid-cid) - (url-unhex-string (match-string 0 mid-cid))))) - (push (list (match-beginning 0) (match-end 0) - (notmuch-id-to-query mid)) links))) - (pcase-dolist (`(,beg ,end ,link) links) - ;; Remove the overlay created by goto-address-mode - (remove-overlays beg end 'goto-address t) - (make-text-button beg end - :type 'notmuch-button-type - 'action `(lambda (arg) - (notmuch-show ,link current-prefix-arg)) - 'follow-link t - 'help-echo "Mouse-1, RET: search for this message" - 'face goto-address-mail-face))))) - -;;; Show command - -;;;###autoload -(defun notmuch-show (thread-id &optional elide-toggle parent-buffer query-context buffer-name) - "Run \"notmuch show\" with the given thread ID and display results. - -ELIDE-TOGGLE, if non-nil, inverts the default elide behavior. - -The optional PARENT-BUFFER is the notmuch-search buffer from -which this notmuch-show command was executed, (so that the -next thread from that buffer can be show when done with this -one). - -The optional QUERY-CONTEXT is a notmuch search term. Only -messages from the thread matching this search term are shown if -non-nil. - -The optional BUFFER-NAME provides the name of the buffer in -which the message thread is shown. If it is nil (which occurs -when the command is called interactively) the argument to the -function is used. - -Returns the buffer containing the messages, or NIL if no messages -matched." - (interactive "sNotmuch show: \nP") - (let ((buffer-name (generate-new-buffer-name - (or buffer-name - (concat "*notmuch-" thread-id "*")))) - (mm-inline-override-types (notmuch--inline-override-types))) - - (pop-to-buffer-same-window (get-buffer-create buffer-name)) - ;; No need to track undo information for this buffer. - (setq buffer-undo-list t) - (notmuch-show-mode) - ;; Set various buffer local variables to their appropriate initial - ;; state. Do this after enabling `notmuch-show-mode' so that they - ;; aren't wiped out. - (setq notmuch-show-thread-id thread-id) - (setq notmuch-show-parent-buffer parent-buffer) - (setq notmuch-show-query-context - (if (or (string= query-context "") - (string= query-context "*")) - nil - query-context)) - (setq notmuch-show-process-crypto notmuch-crypto-process-mime) - ;; If `elide-toggle', invert the default value. - (setq notmuch-show-elide-non-matching-messages - (if elide-toggle - (not notmuch-show-only-matching-messages) - notmuch-show-only-matching-messages)) - (add-hook 'post-command-hook #'notmuch-show-command-hook nil t) - (jit-lock-register #'notmuch-show-buttonise-links) - (notmuch-tag-clear-cache) - (let ((inhibit-read-only t)) - (if (notmuch-show--build-buffer) - ;; Messages were inserted into the buffer. - (current-buffer) - ;; No messages were inserted - presumably none matched the - ;; query. - (kill-buffer (current-buffer)) - (ding) - (message "No messages matched the query!") - nil)))) - -(defun notmuch-show--build-queries (thread context) - "Return a list of queries to try for this search. - -THREAD and CONTEXT are both strings, though CONTEXT may be nil. -When CONTEXT is not nil, the first query is the conjunction of it -and THREAD. The next query is THREAD alone, and serves as a -fallback if the prior matches no messages." - (let (queries) - (push (list thread) queries) - (when context - (push (list thread "and (" context ")") queries)) - queries)) - -(defun notmuch-show--header-line-format () - "Compute the header line format of a notmuch-show buffer." - (when notmuch-show-header-line - (let* ((s (notmuch-sanitize - (notmuch-show-strip-re (notmuch-show-get-subject)))) - (subject (replace-regexp-in-string "%" "%%" s))) - (cond ((stringp notmuch-show-header-line) - (format-spec notmuch-show-header-line `((?s . ,subject)))) - ((functionp notmuch-show-header-line) - (funcall notmuch-show-header-line subject)) - (notmuch-show-header-line subject))))) - -(defun notmuch-show--build-buffer (&optional state) - "Display messages matching the current buffer context. - -Apply the previously saved STATE if supplied, otherwise show the -first relevant message. - -If no messages match the query return NIL." - (let* ((cli-args (list "--exclude=false")) - (cli-args (if notmuch-show-elide-non-matching-messages (cons "--entire-thread=false" cli-args) cli-args)) - ;; "part 0 is the whole message (headers and body)" notmuch-show(1) - (cli-args (if notmuch-show-single-message (cons "--part=0" cli-args) cli-args)) - (queries (notmuch-show--build-queries - notmuch-show-thread-id notmuch-show-query-context)) - (forest nil) - ;; Must be reset every time we are going to start inserting - ;; messages into the buffer. - (notmuch-show-previous-subject "")) - ;; Use results from the first query that returns some. - (while (and (not forest) queries) - (setq forest (notmuch--run-show - (append cli-args (list "'") (car queries) (list "'")))) - (when (and forest notmuch-show-single-message) - (setq forest (list (list (list forest))))) - (setq queries (cdr queries))) - (when forest - (notmuch-show-insert-forest forest) - ;; Store the original tags for each message so that we can - ;; display changes. - (notmuch-show-mapc - (lambda () (notmuch-show-set-prop :orig-tags (notmuch-show-get-tags)))) - (setq header-line-format (notmuch-show--header-line-format)) - (run-hooks 'notmuch-show-hook) - (if state - (notmuch-show-apply-state state) - ;; With no state to apply, just go to the first message. - (notmuch-show-goto-first-wanted-message))) - ;; Report back to the caller whether any messages matched. - forest)) - -;;; Refresh command - -(defun notmuch-show-capture-state () - "Capture the state of the current buffer. - -This includes: - - the list of open messages, - - the combination of current message id with/for each visible window." - (let* ((win-list (get-buffer-window-list (current-buffer) nil t)) - (win-id-combo (mapcar (lambda (win) - (with-selected-window win - (list win (notmuch-show-get-message-id)))) - win-list))) - (list win-id-combo (notmuch-show-get-message-ids-for-open-messages)))) - -(defun notmuch-show-get-query () - "Return the current query in this show buffer." - (if notmuch-show-query-context - (concat notmuch-show-thread-id - " and (" - notmuch-show-query-context - ")") - notmuch-show-thread-id)) - -(defun notmuch-show-goto-message (msg-id) - "Go to message with msg-id." - (goto-char (point-min)) - (unless (cl-loop if (string= msg-id (notmuch-show-get-message-id)) - return t - until (not (notmuch-show-goto-message-next))) - (goto-char (point-min)) - (message "Message-id not found.")) - (notmuch-show-message-adjust)) - -(defun notmuch-show-apply-state (state) - "Apply STATE to the current buffer. - -This includes: - - opening the messages previously opened, - - closing all other messages, - - moving to the correct current message in every displayed window." - (let ((win-msg-alist (car state)) - (open (cadr state))) - ;; Open those that were open. - (goto-char (point-min)) - (cl-loop do (notmuch-show-message-visible - (notmuch-show-get-message-properties) - (member (notmuch-show-get-message-id) open)) - until (not (notmuch-show-goto-message-next))) - (dolist (win-msg-pair win-msg-alist) - (with-selected-window (car win-msg-pair) - ;; Go to the previously open message in this window - (notmuch-show-goto-message (cadr win-msg-pair)))))) - -(defun notmuch-show-refresh-view (&optional reset-state) - "Refresh the current view. - -Refreshes the current view, observing changes in display -preferences. If invoked with a prefix argument (or RESET-STATE is -non-nil) then the state of the buffer (open/closed messages) is -reset based on the original query." - (interactive "P") - (let ((inhibit-read-only t) - (mm-inline-override-types (notmuch--inline-override-types)) - (state (unless reset-state - (notmuch-show-capture-state)))) - ;; `erase-buffer' does not seem to remove overlays, which can lead - ;; to weird effects such as remaining images, so remove them - ;; manually. - (remove-overlays) - (erase-buffer) - (unless (notmuch-show--build-buffer state) - ;; No messages were inserted. - (kill-buffer (current-buffer)) - (ding) - (message "Refreshing the buffer resulted in no messages!")))) - -;;; Keymaps - -(defvar notmuch-show-stash-map - (let ((map (make-sparse-keymap))) - (define-key map "c" 'notmuch-show-stash-cc) - (define-key map "d" 'notmuch-show-stash-date) - (define-key map "F" 'notmuch-show-stash-filename) - (define-key map "f" 'notmuch-show-stash-from) - (define-key map "i" 'notmuch-show-stash-message-id) - (define-key map "I" 'notmuch-show-stash-message-id-stripped) - (define-key map "s" 'notmuch-show-stash-subject) - (define-key map "T" 'notmuch-show-stash-tags) - (define-key map "t" 'notmuch-show-stash-to) - (define-key map "l" 'notmuch-show-stash-mlarchive-link) - (define-key map "L" 'notmuch-show-stash-mlarchive-link-and-go) - (define-key map "G" 'notmuch-show-stash-git-send-email) - (define-key map "?" 'notmuch-subkeymap-help) - map) - "Submap for stash commands.") -(fset 'notmuch-show-stash-map notmuch-show-stash-map) - -(defvar notmuch-show-part-map - (let ((map (make-sparse-keymap))) - (define-key map "s" 'notmuch-show-save-part) - (define-key map "v" 'notmuch-show-view-part) - (define-key map "o" 'notmuch-show-interactively-view-part) - (define-key map "|" 'notmuch-show-pipe-part) - (define-key map "m" 'notmuch-show-choose-mime-of-part) - (define-key map "?" 'notmuch-subkeymap-help) - map) - "Submap for part commands.") -(fset 'notmuch-show-part-map notmuch-show-part-map) - -(defvar notmuch-show-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map notmuch-common-keymap) - (define-key map "Z" 'notmuch-tree-from-show-current-query) - (define-key map "U" 'notmuch-unthreaded-from-show-current-query) - (define-key map (kbd "<C-tab>") 'widget-backward) - (define-key map (kbd "M-TAB") 'notmuch-show-previous-button) - (define-key map (kbd "<backtab>") 'notmuch-show-previous-button) - (define-key map (kbd "TAB") 'notmuch-show-next-button) - (define-key map "f" 'notmuch-show-forward-message) - (define-key map "F" 'notmuch-show-forward-open-messages) - (define-key map "b" 'notmuch-show-resend-message) - (define-key map "l" 'notmuch-show-filter-thread) - (define-key map "r" 'notmuch-show-reply-sender) - (define-key map "R" 'notmuch-show-reply) - (define-key map "|" 'notmuch-show-pipe-message) - (define-key map "w" 'notmuch-show-save-attachments) - (define-key map "V" 'notmuch-show-view-raw-message) - (define-key map "e" 'notmuch-show-resume-message) - (define-key map "c" 'notmuch-show-stash-map) - (define-key map "h" 'notmuch-show-toggle-visibility-headers) - (define-key map "k" 'notmuch-tag-jump) - (define-key map "*" 'notmuch-show-tag-all) - (define-key map "-" 'notmuch-show-remove-tag) - (define-key map "+" 'notmuch-show-add-tag) - (define-key map "X" 'notmuch-show-archive-thread-then-exit) - (define-key map "x" 'notmuch-show-archive-message-then-next-or-exit) - (define-key map "A" 'notmuch-show-archive-thread-then-next) - (define-key map "a" 'notmuch-show-archive-message-then-next-or-next-thread) - (define-key map "N" 'notmuch-show-next-message) - (define-key map "P" 'notmuch-show-previous-message) - (define-key map "n" 'notmuch-show-next-open-message) - (define-key map "p" 'notmuch-show-previous-open-message) - (define-key map (kbd "M-n") 'notmuch-show-next-thread-show) - (define-key map (kbd "M-p") 'notmuch-show-previous-thread-show) - (define-key map (kbd "DEL") 'notmuch-show-rewind) - (define-key map " " 'notmuch-show-advance-and-archive) - (define-key map (kbd "M-RET") 'notmuch-show-open-or-close-all) - (define-key map (kbd "RET") 'notmuch-show-toggle-message) - (define-key map "#" 'notmuch-show-print-message) - (define-key map "!" 'notmuch-show-toggle-elide-non-matching) - (define-key map "$" 'notmuch-show-toggle-process-crypto) - (define-key map "%" 'notmuch-show-choose-duplicate) - (define-key map "<" 'notmuch-show-toggle-thread-indentation) - (define-key map "t" 'toggle-truncate-lines) - (define-key map "." 'notmuch-show-part-map) - (define-key map "B" 'notmuch-show-browse-urls) - map) - "Keymap for \"notmuch show\" buffers.") - -;;; Mode - -(define-derived-mode notmuch-show-mode fundamental-mode "notmuch-show" - "Major mode for viewing a thread with notmuch. - -This buffer contains the results of the \"notmuch show\" command -for displaying a single thread of email from your email archives. - -By default, various components of email messages, (citations, -signatures, already-read messages), are hidden. You can make -these parts visible by clicking with the mouse button or by -pressing RET after positioning the cursor on a hidden part, (for -which \\[notmuch-show-next-button] and \\[notmuch-show-previous-button] are helpful). - -Reading the thread sequentially is well-supported by pressing -\\[notmuch-show-advance-and-archive]. This will scroll the current message (if necessary), advance -to the next message, or advance to the next thread (if already on -the last message of a thread). - -Other commands are available to read or manipulate the thread -more selectively, (such as '\\[notmuch-show-next-message]' and '\\[notmuch-show-previous-message]' to advance to messages -without removing any tags, and '\\[notmuch-show-archive-thread]' to archive an entire thread -without scrolling through with \\[notmuch-show-advance-and-archive]). - -You can add or remove arbitrary tags from the current message with -'\\[notmuch-show-add-tag]' or '\\[notmuch-show-remove-tag]'. - -All currently available key bindings: - -\\{notmuch-show-mode-map}" - (setq notmuch-buffer-refresh-function #'notmuch-show-refresh-view) - (setq buffer-read-only t) - (setq truncate-lines t) - (setq imenu-prev-index-position-function - #'notmuch-show-imenu-prev-index-position-function) - (setq imenu-extract-index-name-function - #'notmuch-show-imenu-extract-index-name-function)) - -;;; Tree commands - -(defun notmuch-tree-from-show-current-query () - "Call notmuch tree with the current query." - (interactive) - (notmuch-tree notmuch-show-thread-id - notmuch-show-query-context - (notmuch-show-get-message-id))) - -(defun notmuch-unthreaded-from-show-current-query () - "Call notmuch unthreaded with the current query." - (interactive) - (notmuch-unthreaded notmuch-show-thread-id - notmuch-show-query-context - (notmuch-show-get-message-id))) - -;;; Movement related functions. - -(defun notmuch-show-move-to-message-top () - (goto-char (notmuch-show-message-top))) - -(defun notmuch-show-move-to-message-bottom () - (goto-char (notmuch-show-message-bottom))) - -;; There's some strangeness here where a text property applied to a -;; region a->b is not found when point is at b. We walk backwards -;; until finding the property. -(defun notmuch-show-message-extent () - "Return a cons cell containing the start and end buffer offset -of the current message." - (let (r) - (save-excursion - (while (not (setq r (get-text-property (point) :notmuch-message-extent))) - (backward-char))) - r)) - -(defun notmuch-show-message-top () - (car (notmuch-show-message-extent))) - -(defun notmuch-show-message-bottom () - (cdr (notmuch-show-message-extent))) - -(defun notmuch-show-goto-message-next () - (let ((start (point))) - (notmuch-show-move-to-message-bottom) - (if (not (eobp)) - t - (goto-char start) - nil))) - -(defun notmuch-show-goto-message-previous () - (notmuch-show-move-to-message-top) - (if (bobp) - nil - (backward-char) - (notmuch-show-move-to-message-top) - t)) - -(defun notmuch-show-mapc (function) - "Iterate through all messages in the current thread with -`notmuch-show-goto-message-next' and call FUNCTION for side -effects." - (save-excursion - (goto-char (point-min)) - (cl-loop do (funcall function) - while (notmuch-show-goto-message-next)))) - -;;; Functions relating to the visibility of messages and their components. - -(defun notmuch-show-message-visible (props visible-p) - (overlay-put (plist-get props :message-overlay) 'invisible (not visible-p)) - (notmuch-show-set-prop :message-visible visible-p props)) - -(defun notmuch-show-headers-visible (props visible-p) - (overlay-put (plist-get props :headers-overlay) 'invisible (not visible-p)) - (notmuch-show-set-prop :headers-visible visible-p props)) - -;;; Functions for setting and getting attributes of the current message. - -(defun notmuch-show-set-message-properties (props) - (save-excursion - (notmuch-show-move-to-message-top) - (put-text-property (point) (+ (point) 1) - :notmuch-message-properties props))) - -(defun notmuch-show-get-message-properties () - "Return the properties of the current message as a plist. - -Some useful entries are: -:headers - Property list containing the headers :Date, :Subject, :From, etc. -:body - Body of the message -:tags - Tags for this message" - (save-excursion - (notmuch-show-move-to-message-top) - (get-text-property (point) :notmuch-message-properties))) - -(defun notmuch-show-get-part-properties () - "Return the properties of the innermost part containing point. - -This is the part property list retrieved from the CLI. Signals -an error if there is no part containing point." - (or (get-text-property (point) :notmuch-part) - (error "No message part here"))) - -(defun notmuch-show-set-prop (prop val &optional props) - (let ((inhibit-read-only t) - (props (or props - (notmuch-show-get-message-properties)))) - (plist-put props prop val) - (notmuch-show-set-message-properties props))) - -(defun notmuch-show-get-prop (prop &optional props) - "Get property PROP from current message in show or tree mode. - -It gets property PROP from PROPS or, if PROPS is nil, the current -message in either tree or show. This means that several utility -functions in notmuch-show can be used directly by notmuch-tree as -they just need the correct message properties." - (plist-get (or props - (cond ((eq major-mode 'notmuch-show-mode) - (notmuch-show-get-message-properties)) - ((eq major-mode 'notmuch-tree-mode) - (notmuch-tree-get-message-properties)) - (t nil))) - prop)) - -(defun notmuch-show-get-message-id (&optional bare) - "Return an id: query for the Message-Id of the current message. - -If optional argument BARE is non-nil, return -the Message-Id without id: prefix and escaping." - (if bare - (notmuch-show-get-prop :id) - (notmuch-id-to-query (notmuch-show-get-prop :id)))) - -(defun notmuch-show-get-messages-ids () - "Return all id: queries of messages in the current thread." - (let ((message-ids)) - (notmuch-show-mapc - (lambda () (push (notmuch-show-get-message-id) message-ids))) - message-ids)) - -(defun notmuch-show-get-messages-ids-search () - "Return a search string for all message ids of messages in the -current thread." - (mapconcat 'identity (notmuch-show-get-messages-ids) " or ")) - -;; dme: Would it make sense to use a macro for many of these? - -(defun notmuch-show-get-filename () - "Return the filename of the current message." - (let ((duplicate (notmuch-show-get-duplicate))) - (nth (1- duplicate) (notmuch-show-get-prop :filename)))) - -(defun notmuch-show-get-header (header &optional props) - "Return the named header of the current message, if any." - (plist-get (notmuch-show-get-prop :headers props) header)) - -(defun notmuch-show-get-cc () - (notmuch-show-get-header :Cc)) - -(defun notmuch-show-get-date () - (notmuch-show-get-header :Date)) - -(defun notmuch-show-get-duplicate () - ;; if no duplicate property exists, assume first file - (or (notmuch-show-get-prop :duplicate) 1)) - -(defun notmuch-show-get-timestamp () - (notmuch-show-get-prop :timestamp)) - -(defun notmuch-show-get-from () - (notmuch-show-get-header :From)) - -(defun notmuch-show-get-subject () - (notmuch-show-get-header :Subject)) - -(defun notmuch-show-get-to () - (notmuch-show-get-header :To)) - -(defun notmuch-show-get-depth () - (notmuch-show-get-prop :depth)) - -(defun notmuch-show-set-tags (tags) - "Set the tags of the current message." - (notmuch-show-set-prop :tags tags) - (notmuch-show-update-tags tags)) - -(defun notmuch-show-get-tags () - "Return the tags of the current message." - (notmuch-show-get-prop :tags)) - -(defun notmuch-show-message-visible-p () - "Is the current message visible?" - (notmuch-show-get-prop :message-visible)) - -(defun notmuch-show-headers-visible-p () - "Are the headers of the current message visible?" - (notmuch-show-get-prop :headers-visible)) - -(put 'notmuch-show-mark-read 'notmuch-prefix-doc - "Mark the current message as unread.") -(defun notmuch-show-mark-read (&optional unread) - "Mark the current message as read. - -Mark the current message as read by applying the tag changes in -`notmuch-show-mark-read-tags' to it (remove the \"unread\" tag by -default). If a prefix argument is given, the message will be -marked as unread, i.e. the tag changes in -`notmuch-show-mark-read-tags' will be reversed." - (interactive "P") - (when notmuch-show-mark-read-tags - (apply 'notmuch-show-tag-message - (notmuch-tag-change-list notmuch-show-mark-read-tags unread)))) - -(defun notmuch-show-seen-current-message (_start _end) - "Mark the current message read if it is open. - -We only mark it read once: if it is changed back then that is a -user decision and we should not override it." - (when (and (notmuch-show-message-visible-p) - (not (notmuch-show-get-prop :seen))) - (notmuch-show-mark-read) - (notmuch-show-set-prop :seen t))) - -(defvar notmuch-show--seen-has-errored nil) -(make-variable-buffer-local 'notmuch-show--seen-has-errored) - -(defun notmuch-show-command-hook () - (when (eq major-mode 'notmuch-show-mode) - ;; We need to redisplay to get window-start and window-end correct. - (redisplay) - (save-excursion - (condition-case nil - (funcall notmuch-show-mark-read-function (window-start) (window-end)) - ((debug error) - (unless notmuch-show--seen-has-errored - (setq notmuch-show--seen-has-errored t) - (setq header-line-format - (concat header-line-format - (propertize - " [some mark read tag changes may have failed]" - 'face font-lock-warning-face))))))))) - -(defun notmuch-show-filter-thread (query) - "Filter or LIMIT the current thread based on a new query string. - -Reshows the current thread with matches defined by the new query-string." - (interactive (list (notmuch-read-query "Filter thread: "))) - (let ((msg-id (notmuch-show-get-message-id))) - (setq notmuch-show-query-context (if (string-empty-p query) nil query)) - (notmuch-show-refresh-view t) - (notmuch-show-goto-message msg-id))) - -;;; Functions for getting attributes of several messages in the current thread. - -(defun notmuch-show-get-message-ids-for-open-messages () - "Return a list of all id: queries for open messages in the current thread." - (save-excursion - (let (message-ids done) - (goto-char (point-min)) - (while (not done) - (when (notmuch-show-message-visible-p) - (setq message-ids - (append message-ids (list (notmuch-show-get-message-id))))) - (setq done (not (notmuch-show-goto-message-next)))) - message-ids))) - -;;; Commands typically bound to keys. - -(defun notmuch-show-advance () - "Advance through thread. - -If the current message in the thread is not yet fully visible, -scroll by a near screenful to read more of the message. - -Otherwise, (the end of the current message is already within the -current window), advance to the next open message." - (interactive) - (let* ((end-of-this-message (notmuch-show-message-bottom)) - (visible-end-of-this-message (1- end-of-this-message)) - (ret nil)) - (while (invisible-p visible-end-of-this-message) - (setq visible-end-of-this-message - (max (point-min) - (1- (previous-single-char-property-change - visible-end-of-this-message 'invisible))))) - (cond - ;; Ideally we would test `end-of-this-message' against the result - ;; of `window-end', but that doesn't account for the fact that - ;; the end of the message might be hidden. - ((and visible-end-of-this-message - (> visible-end-of-this-message (window-end))) - ;; The bottom of this message is not visible - scroll. - (scroll-up nil)) - ((not (= end-of-this-message (point-max))) - ;; This is not the last message - move to the next visible one. - (notmuch-show-next-open-message)) - ((not (= (point) (point-max))) - ;; This is the last message, but the cursor is not at the end of - ;; the buffer. Move it there. - (goto-char (point-max))) - (t - ;; This is the last message - change the return value - (setq ret t))) - ret)) - -(defun notmuch-show-advance-and-archive () - "Advance through thread and archive. - -This command is intended to be one of the simplest ways to -process a thread of email. It works exactly like -notmuch-show-advance, in that it scrolls through messages in a -show buffer, except that when it gets to the end of the buffer it -archives the entire current thread, (apply changes in -`notmuch-archive-tags'), kills the buffer, and displays the next -thread from the search from which this thread was originally -shown." - (interactive) - (when (notmuch-show-advance) - (notmuch-show-archive-thread-then-next))) - -(defun notmuch-show-rewind () - "Backup through the thread (reverse scrolling compared to \ -\\[notmuch-show-advance-and-archive]). - -Specifically, if the beginning of the previous email is fewer -than `window-height' lines from the current point, move to it -just like `notmuch-show-previous-message'. - -Otherwise, just scroll down a screenful of the current message. - -This command does not modify any message tags, (it does not undo -any effects from previous calls to -`notmuch-show-advance-and-archive'." - (interactive) - (let ((start-of-message (notmuch-show-message-top)) - (start-of-window (window-start))) - (cond - ;; Either this message is properly aligned with the start of the - ;; window or the start of this message is not visible on the - ;; screen - scroll. - ((or (= start-of-message start-of-window) - (< start-of-message start-of-window)) - (scroll-down) - ;; If a small number of lines from the previous message are - ;; visible, realign so that the top of the current message is at - ;; the top of the screen. - (when (<= (count-screen-lines (window-start) start-of-message) - next-screen-context-lines) - (goto-char (notmuch-show-message-top)) - (notmuch-show-message-adjust)) - ;; Move to the top left of the window. - (goto-char (window-start))) - (t - ;; Move to the previous message. - (notmuch-show-previous-message))))) - -(put 'notmuch-show-reply 'notmuch-prefix-doc "... and prompt for sender") -(defun notmuch-show-reply (&optional prompt-for-sender) - "Reply to the sender and all recipients of the current message." - (interactive "P") - (notmuch-mua-new-reply (notmuch-show-get-message-id) prompt-for-sender t - (notmuch-show-get-prop :duplicate))) - -(put 'notmuch-show-reply-sender 'notmuch-prefix-doc "... and prompt for sender") -(defun notmuch-show-reply-sender (&optional prompt-for-sender) - "Reply to the sender of the current message." - (interactive "P") - (notmuch-mua-new-reply (notmuch-show-get-message-id) prompt-for-sender nil - (notmuch-show-get-prop :duplicate))) - -(put 'notmuch-show-forward-message 'notmuch-prefix-doc - "... and prompt for sender") -(defun notmuch-show-forward-message (&optional prompt-for-sender) - "Forward the current message." - (interactive "P") - (notmuch-mua-new-forward-messages (list (notmuch-show-get-message-id)) - prompt-for-sender)) - -(put 'notmuch-show-forward-open-messages 'notmuch-prefix-doc - "... and prompt for sender") -(defun notmuch-show-forward-open-messages (&optional prompt-for-sender) - "Forward the currently open messages." - (interactive "P") - (let ((open-messages (notmuch-show-get-message-ids-for-open-messages))) - (unless open-messages - (error "No open messages to forward.")) - (notmuch-mua-new-forward-messages open-messages prompt-for-sender))) - -(defun notmuch-show-resend-message (addresses) - "Resend the current message." - (interactive (list (notmuch-address-from-minibuffer "Resend to: "))) - (when (y-or-n-p (concat "Confirm resend to " addresses " ")) - (notmuch-show-view-raw-message) - (message-resend addresses) - (notmuch-bury-or-kill-this-buffer))) - -(defun notmuch-show-message-adjust () - (recenter 0)) - -(defun notmuch-show-next-message (&optional pop-at-end) - "Show the next message. - -If a prefix argument is given and this is the last message in the -thread, navigate to the next thread in the parent search buffer." - (interactive "P") - (if (notmuch-show-goto-message-next) - (notmuch-show-message-adjust) - (if pop-at-end - (notmuch-show-next-thread) - (goto-char (point-max))))) - -(defun notmuch-show-previous-message () - "Show the previous message or the start of the current message." - (interactive) - (if (= (point) (notmuch-show-message-top)) - (notmuch-show-goto-message-previous) - (notmuch-show-move-to-message-top)) - (notmuch-show-message-adjust)) - -(defun notmuch-show-next-open-message (&optional pop-at-end) - "Show the next open message. - -If a prefix argument is given and this is the last open message -in the thread, navigate to the next thread in the parent search -buffer. Return t if there was a next open message in the thread -to show, nil otherwise." - (interactive "P") - (let (r) - (while (and (setq r (notmuch-show-goto-message-next)) - (not (notmuch-show-message-visible-p)))) - (if r - (notmuch-show-message-adjust) - (if pop-at-end - (notmuch-show-next-thread) - (goto-char (point-max)))) - r)) - -(defun notmuch-show-next-matching-message () - "Show the next matching message." - (interactive) - (let (r) - (while (and (setq r (notmuch-show-goto-message-next)) - (not (notmuch-show-get-prop :match)))) - (if r - (notmuch-show-message-adjust) - (goto-char (point-max))))) - -(defun notmuch-show-open-if-matched () - "Open a message if it is matched (whether or not excluded)." - (let ((props (notmuch-show-get-message-properties))) - (notmuch-show-message-visible props (plist-get props :match)))) - -(defun notmuch-show-goto-first-wanted-message () - "Move to the first open message and mark it read." - (goto-char (point-min)) - (unless (notmuch-show-message-visible-p) - (notmuch-show-next-open-message)) - (when (eobp) - ;; There are no matched non-excluded messages so open all matched - ;; (necessarily excluded) messages and go to the first. - (notmuch-show-mapc 'notmuch-show-open-if-matched) - (force-window-update) - (goto-char (point-min)) - (unless (notmuch-show-message-visible-p) - (notmuch-show-next-open-message)))) - -(defun notmuch-show-previous-open-message () - "Show the previous open message." - (interactive) - (while (and (if (= (point) (notmuch-show-message-top)) - (notmuch-show-goto-message-previous) - (notmuch-show-move-to-message-top)) - (not (notmuch-show-message-visible-p)))) - (notmuch-show-message-adjust)) - -(defun notmuch-show-view-raw-message () - "View the original source of the current message." - (interactive) - (let* ((id (notmuch-show-get-message-id)) - (duplicate (notmuch-show-get-duplicate)) - (args (if (> duplicate 1) - (list (format "--duplicate=%d" duplicate) id) - (list id))) - (buf (get-buffer-create (format "*notmuch-raw-%s-%d*" id duplicate))) - (inhibit-read-only t)) - (pop-to-buffer-same-window buf) - (erase-buffer) - (let ((coding-system-for-read 'no-conversion)) - (apply #'notmuch--call-process notmuch-command nil t nil "show" "--format=raw" args)) - (goto-char (point-min)) - (set-buffer-modified-p nil) - (setq buffer-read-only t) - (view-buffer buf 'kill-buffer-if-not-modified))) - -(defun notmuch-show-resume-message () - "Resume EDITING the current draft message." - (interactive) - (notmuch-draft-resume (notmuch-show-get-message-id))) - -(put 'notmuch-show-pipe-message 'notmuch-doc - "Pipe the contents of the current message to a command.") -(put 'notmuch-show-pipe-message 'notmuch-prefix-doc - "Pipe the thread as an mbox to a command.") -(defun notmuch-show-pipe-message (entire-thread command) - "Pipe the contents of the current message (or thread) to COMMAND. - -COMMAND will be executed with the raw contents of the current -email message as stdin. Anything printed by the command to stdout -or stderr will appear in the *notmuch-pipe* buffer. - -If ENTIRE-THREAD is non-nil (or when invoked with a prefix -argument), COMMAND will receive all open messages in the current -thread (formatted as an mbox) rather than only the current -message." - (interactive (let ((query-string (if current-prefix-arg - "Pipe all open messages to command: " - "Pipe message to command: "))) - (list current-prefix-arg (read-shell-command query-string)))) - (let (shell-command) - (if entire-thread - (setq shell-command - (concat notmuch-command " show --format=mbox --exclude=false " - (shell-quote-argument - (mapconcat 'identity - (notmuch-show-get-message-ids-for-open-messages) - " OR ")) - " | " command)) - (setq shell-command - (concat notmuch-command " show --format=raw " - (shell-quote-argument (notmuch-show-get-message-id)) - " | " command))) - (let ((cwd default-directory) - (buf (get-buffer-create (concat "*notmuch-pipe*")))) - (with-current-buffer buf - (setq buffer-read-only t) - (let ((inhibit-read-only t)) - (erase-buffer) - ;; Use the originating buffer's working directory instead of - ;; that of the pipe buffer. - (cd cwd) - (let ((exit-code (call-process-shell-command shell-command nil buf))) - (goto-char (point-max)) - (set-buffer-modified-p nil) - (unless (zerop exit-code) - (pop-to-buffer buf) - (message (format "Command '%s' exited abnormally with code %d" - shell-command exit-code))))))))) - -(defun notmuch-show-tag-message (&rest tag-changes) - "Change tags for the current message. - -TAG-CHANGES is a list of tag operations for `notmuch-tag'." - (let* ((current-tags (notmuch-show-get-tags)) - (new-tags (notmuch-update-tags current-tags tag-changes))) - (unless (equal current-tags new-tags) - (notmuch-tag (notmuch-show-get-message-id) tag-changes) - (notmuch-show-set-tags new-tags)))) - -(defun notmuch-show-tag (tag-changes) - "Change tags for the current message. - -See `notmuch-tag' for information on the format of TAG-CHANGES." - (interactive (list (notmuch-read-tag-changes (notmuch-show-get-tags) - "Tag message"))) - (notmuch-tag (notmuch-show-get-message-id) tag-changes) - (let* ((current-tags (notmuch-show-get-tags)) - (new-tags (notmuch-update-tags current-tags tag-changes))) - (unless (equal current-tags new-tags) - (notmuch-show-set-tags new-tags)))) - -(defun notmuch-show-tag-all (tag-changes) - "Change tags for all messages in the current show buffer. - -See `notmuch-tag' for information on the format of TAG-CHANGES." - (interactive - (list (let (tags) - (notmuch-show-mapc - (lambda () (setq tags (append (notmuch-show-get-tags) tags)))) - (notmuch-read-tag-changes tags "Tag thread")))) - (notmuch-tag (notmuch-show-get-messages-ids-search) tag-changes) - (notmuch-show-mapc - (lambda () - (let* ((current-tags (notmuch-show-get-tags)) - (new-tags (notmuch-update-tags current-tags tag-changes))) - (unless (equal current-tags new-tags) - (notmuch-show-set-tags new-tags)))))) - -(defun notmuch-show-add-tag (tag-changes) - "Change tags for the current message (defaulting to add). - -Same as `notmuch-show-tag' but sets initial input to '+'." - (interactive - (list (notmuch-read-tag-changes (notmuch-show-get-tags) "Tag message" "+"))) - (notmuch-show-tag tag-changes)) - -(defun notmuch-show-remove-tag (tag-changes) - "Change tags for the current message (defaulting to remove). - -Same as `notmuch-show-tag' but sets initial input to '-'." - (interactive - (list (notmuch-read-tag-changes (notmuch-show-get-tags) "Tag message" "-"))) - (notmuch-show-tag tag-changes)) - -(defun notmuch-show-toggle-visibility-headers () - "Toggle the visibility of the current message headers." - (interactive) - (let ((props (notmuch-show-get-message-properties))) - (notmuch-show-headers-visible - props - (not (plist-get props :headers-visible)))) - (force-window-update)) - -(defun notmuch-show-toggle-message () - "Toggle the visibility of the current message." - (interactive) - (let ((props (notmuch-show-get-message-properties))) - (notmuch-show-message-visible - props - (not (plist-get props :message-visible)))) - (force-window-update)) - -(put 'notmuch-show-open-or-close-all 'notmuch-doc "Show all messages.") -(put 'notmuch-show-open-or-close-all 'notmuch-prefix-doc "Hide all messages.") -(defun notmuch-show-open-or-close-all () - "Set the visibility all of the messages in the current thread. - -By default make all of the messages visible. With a prefix -argument, hide all of the messages." - (interactive) - (save-excursion - (goto-char (point-min)) - (cl-loop do (notmuch-show-message-visible - (notmuch-show-get-message-properties) - (not current-prefix-arg)) - until (not (notmuch-show-goto-message-next)))) - (force-window-update)) - -(defun notmuch-show-next-button () - "Advance point to the next button in the buffer." - (interactive) - (forward-button 1)) - -(defun notmuch-show-previous-button () - "Move point back to the previous button in the buffer." - (interactive) - (backward-button 1)) - -(defun notmuch-show-next-thread (&optional show previous) - "Move to the next item in the search results, if any. - -If SHOW is non-nil, open the next item in a show -buffer. Otherwise just highlight the next item in the search -buffer. If PREVIOUS is non-nil, move to the previous item in the -search results instead. - -Return non-nil on success." - (interactive "P") - (let ((parent-buffer notmuch-show-parent-buffer)) - (notmuch-bury-or-kill-this-buffer) - (when (buffer-live-p parent-buffer) - (switch-to-buffer parent-buffer) - (and (if previous - (notmuch-search-previous-thread) - (notmuch-search-next-thread)) - show - (notmuch-search-show-thread))))) - -(defun notmuch-show-next-thread-show () - "Show the next thread in the search results, if any." - (interactive) - (notmuch-show-next-thread t)) - -(defun notmuch-show-previous-thread-show () - "Show the previous thread in the search results, if any." - (interactive) - (notmuch-show-next-thread t t)) - -(put 'notmuch-show-archive-thread 'notmuch-prefix-doc - "Un-archive each message in thread.") -(defun notmuch-show-archive-thread (&optional unarchive) - "Archive each message in thread. - -Archive each message currently shown by applying the tag changes -in `notmuch-archive-tags' to each. If a prefix argument is given, -the messages will be \"unarchived\", i.e. the tag changes in -`notmuch-archive-tags' will be reversed. - -Note: This command is safe from any race condition of new messages -being delivered to the same thread. It does not archive the -entire thread, but only the messages shown in the current -buffer." - (interactive "P") - (when notmuch-archive-tags - (notmuch-show-tag-all - (notmuch-tag-change-list notmuch-archive-tags unarchive)))) - -(defun notmuch-show-archive-thread-then-next () - "Archive all messages in the current buffer, then show next thread from search." - (interactive) - (notmuch-show-archive-thread) - (notmuch-show-next-thread t)) - -(defun notmuch-show-archive-thread-then-exit () - "Archive all messages in the current buffer, then exit back to search results." - (interactive) - (notmuch-show-archive-thread) - (notmuch-show-next-thread)) - -(put 'notmuch-show-archive-message 'notmuch-prefix-doc - "Un-archive the current message.") -(defun notmuch-show-archive-message (&optional unarchive) - "Archive the current message. - -Archive the current message by applying the tag changes in -`notmuch-archive-tags' to it. If a prefix argument is given, the -message will be \"unarchived\", i.e. the tag changes in -`notmuch-archive-tags' will be reversed." - (interactive "P") - (when notmuch-archive-tags - (apply 'notmuch-show-tag-message - (notmuch-tag-change-list notmuch-archive-tags unarchive)))) - -(defun notmuch-show-archive-message-then-next-or-exit () - "Archive current message, then show next open message in current thread. - -If at the last open message in the current thread, then exit back -to search results." - (interactive) - (notmuch-show-archive-message) - (notmuch-show-next-open-message t)) - -(defun notmuch-show-archive-message-then-next-or-next-thread () - "Archive current message, then show next open message in current or next thread. - -If at the last open message in the current thread, then show next -thread from search." - (interactive) - (notmuch-show-archive-message) - (unless (notmuch-show-next-open-message) - (notmuch-show-next-thread t))) - -(defun notmuch-show-stash-cc () - "Copy CC field of current message to kill-ring." - (interactive) - (notmuch-common-do-stash (notmuch-show-get-cc))) - -(put 'notmuch-show-stash-date 'notmuch-prefix-doc - "Copy timestamp of current message to kill-ring.") -(defun notmuch-show-stash-date (&optional stash-timestamp) - "Copy date of current message to kill-ring. - -If invoked with a prefix argument, copy timestamp of current -message to kill-ring." - (interactive "P") - (if stash-timestamp - (notmuch-common-do-stash (format "%d" (notmuch-show-get-timestamp))) - (notmuch-common-do-stash (notmuch-show-get-date)))) - -(defun notmuch-show-stash-filename () - "Copy filename of current message to kill-ring." - (interactive) - (notmuch-common-do-stash (notmuch-show-get-filename))) - -(defun notmuch-show-stash-from () - "Copy From address of current message to kill-ring." - (interactive) - (notmuch-common-do-stash (notmuch-show-get-from))) - -(put 'notmuch-show-stash-message-id 'notmuch-prefix-doc - "Copy thread: query matching current thread to kill-ring.") -(defun notmuch-show-stash-message-id (&optional stash-thread-id) - "Copy id: query matching the current message to kill-ring. - -If invoked with a prefix argument (or STASH-THREAD-ID is -non-nil), copy thread: query matching the current thread to -kill-ring." - (interactive "P") - (if stash-thread-id - (notmuch-common-do-stash notmuch-show-thread-id) - (notmuch-common-do-stash (notmuch-show-get-message-id)))) - -(defun notmuch-show-stash-message-id-stripped () - "Copy message ID of current message (sans `id:' prefix) to kill-ring." - (interactive) - (notmuch-common-do-stash (notmuch-show-get-message-id t))) - -(defun notmuch-show-stash-subject () - "Copy Subject field of current message to kill-ring." - (interactive) - (notmuch-common-do-stash (notmuch-show-get-subject))) - -(defun notmuch-show-stash-tags () - "Copy tags of current message to kill-ring as a comma separated list." - (interactive) - (notmuch-common-do-stash (mapconcat 'identity (notmuch-show-get-tags) ","))) - -(defun notmuch-show-stash-to () - "Copy To address of current message to kill-ring." - (interactive) - (notmuch-common-do-stash (notmuch-show-get-to))) - -(defun notmuch-show-stash-mlarchive-link (&optional mla) - "Copy an ML Archive URI for the current message to the kill-ring. - -This presumes that the message is available at the selected -Mailing List Archive. - -If optional argument MLA is non-nil, use the provided key instead -of prompting the user (see -`notmuch-show-stash-mlarchive-link-alist')." - (interactive) - (let ((url (cdr (assoc - (or mla - (let ((completion-ignore-case t)) - (completing-read - "Mailing List Archive: " - notmuch-show-stash-mlarchive-link-alist - nil t nil nil - notmuch-show-stash-mlarchive-link-default))) - notmuch-show-stash-mlarchive-link-alist)))) - (notmuch-common-do-stash - (if (functionp url) - (funcall url (notmuch-show-get-message-id t)) - (concat url (notmuch-show-get-message-id t)))))) - -(defun notmuch-show-stash-mlarchive-link-and-go (&optional mla) - "Copy an ML Archive URI for the current message to the - kill-ring and visit it. - -This presumes that the message is available at the selected -Mailing List Archive. - -If optional argument MLA is non-nil, use the provided key instead -of prompting the user (see -`notmuch-show-stash-mlarchive-link-alist')." - (interactive) - (notmuch-show-stash-mlarchive-link mla) - (browse-url (current-kill 0 t))) - -(defun notmuch-show-stash-git-helper (addresses prefix) - "Normalize all ADDRESSES while adding PREFIX. -Escape, trim, quote and add PREFIX to each address in list -of ADDRESSES, and return the result as a single string." - (mapconcat (lambda (x) - (concat prefix "\"" - ;; escape double-quotes - (replace-regexp-in-string - "\"" "\\\\\"" - ;; trim leading and trailing spaces - (replace-regexp-in-string - "\\(^ *\\| *$\\)" "" - x)) "\"")) - addresses " ")) - -(put 'notmuch-show-stash-git-send-email 'notmuch-prefix-doc - "Copy From/To/Cc of current message to kill-ring. -Use a form suitable for pasting to git send-email command line.") - -(defun notmuch-show-stash-git-send-email (&optional no-in-reply-to) - "Copy From/To/Cc/Message-Id of current message to kill-ring. -Use a form suitable for pasting to git send-email command line. - -If invoked with a prefix argument (or NO-IN-REPLY-TO is non-nil), -omit --in-reply-to=<Message-Id>." - (interactive "P") - (notmuch-common-do-stash - (mapconcat 'identity - (remove "" - (list - (notmuch-show-stash-git-helper - (message-tokenize-header (notmuch-show-get-from)) "--to=") - (notmuch-show-stash-git-helper - (message-tokenize-header (notmuch-show-get-to)) "--to=") - (notmuch-show-stash-git-helper - (message-tokenize-header (notmuch-show-get-cc)) "--cc=") - (unless no-in-reply-to - (notmuch-show-stash-git-helper - (list (notmuch-show-get-message-id t)) "--in-reply-to=")))) - " "))) - -;;; Interactive part functions and their helpers - -(defun notmuch-show-generate-part-buffer (msg part) - "Return a temporary buffer containing the specified part's content." - (let ((buf (generate-new-buffer " *notmuch-part*")) - (process-crypto notmuch-show-process-crypto)) - (with-current-buffer buf - ;; This is always used in the content of mm handles, which - ;; expect undecoded, binary part content. - (insert (notmuch-get-bodypart-binary msg part process-crypto))) - buf)) - -(defun notmuch-show-current-part-handle (&optional mime-type) - "Return an mm-handle for the part containing point. - -This creates a temporary buffer for the part's content; the -caller is responsible for killing this buffer as appropriate. If -MIME-TYPE is given then set the handle's mime-type to MIME-TYPE." - (let* ((msg (notmuch-show-get-message-properties)) - (part (notmuch-show-get-part-properties)) - (buf (notmuch-show-generate-part-buffer msg part)) - (computed-type (or mime-type (plist-get part :computed-type))) - (filename (plist-get part :filename)) - (disposition (and filename `(attachment (filename . ,filename))))) - (mm-make-handle buf (list computed-type) nil nil disposition))) - -(defun notmuch-show-apply-to-current-part-handle (fn &optional mime-type) - "Apply FN to an mm-handle for the part containing point. - -This ensures that the temporary buffer created for the mm-handle -is destroyed when FN returns. If MIME-TYPE is given then force -part to be treated as if it had that mime-type." - (let ((handle (notmuch-show-current-part-handle mime-type))) - ;; Emacs puts stdout/stderr into the calling buffer so we call - ;; it from a temp-buffer, unless notmuch-show-attachment-debug - ;; is non-nil, in which case we put it in " *notmuch-part*". - (unwind-protect - (if notmuch-show-attachment-debug - (with-current-buffer (generate-new-buffer " *notmuch-part*") - (funcall fn handle)) - (with-temp-buffer - (funcall fn handle))) - (kill-buffer (mm-handle-buffer handle))))) - -(defun notmuch-show-part-button-default (&optional button) - (interactive) - (let ((button (or button (button-at (point))))) - ;; Try to toggle the part, if that fails then call the default - ;; action. The toggle fails if the part has no emacs renderable - ;; content. - (unless (notmuch-show-toggle-part-invisibility button) - (call-interactively notmuch-show-part-button-default-action)))) - -(defun notmuch-show-save-part () - "Save the MIME part containing point to a file." - (interactive) - (notmuch-show-apply-to-current-part-handle #'mm-save-part)) - -(defun notmuch-show-view-part () - "View the MIME part containing point in an external viewer." - (interactive) - ;; Set mm-inlined-types to nil to force an external viewer - (let ((mm-inlined-types nil)) - (notmuch-show-apply-to-current-part-handle #'mm-display-part))) - -(defun notmuch-show-interactively-view-part () - "View the MIME part containing point, prompting for a viewer." - (interactive) - (notmuch-show-apply-to-current-part-handle #'mm-interactively-view-part)) - -(defun notmuch-show-pipe-part () - "Pipe the MIME part containing point to an external command." - (interactive) - (notmuch-show-apply-to-current-part-handle #'mm-pipe-part)) - -(defun notmuch-show--mm-display-part (handle) - "Use mm-display-part to display HANDLE in a new buffer. - -If the part is displayed in an external application then close -the new buffer." - (let ((buf (get-buffer-create (generate-new-buffer-name - (concat " *notmuch-internal-part*"))))) - (pop-to-buffer-same-window buf) - (if (eq (mm-display-part handle) 'external) - (kill-buffer buf) - (goto-char (point-min)) - (set-buffer-modified-p nil) - (view-buffer buf 'kill-buffer-if-not-modified)))) - -(defun notmuch-show-choose-mime-of-part (mime-type) - "Choose the mime type to use for displaying part." - (interactive - (list (completing-read "Mime type to use (default text/plain): " - (mailcap-mime-types) nil nil nil nil "text/plain"))) - (notmuch-show-apply-to-current-part-handle #'notmuch-show--mm-display-part - mime-type)) - -(defun notmuch-show-imenu-prev-index-position-function () - "Move point to previous message in notmuch-show buffer. -This function is used as a value for -`imenu-prev-index-position-function'." - (if (bobp) - nil - (notmuch-show-previous-message) - t)) - -(defun notmuch-show-imenu-extract-index-name-function () - "Return imenu name for line at point. -This function is used as a value for -`imenu-extract-index-name-function'. Point should be at the -beginning of the line." - (back-to-indentation) - (buffer-substring-no-properties (if notmuch-show-imenu-indent - (line-beginning-position) - (point)) - (line-end-position))) - -(defmacro notmuch-show--with-currently-shown-message (&rest body) - "Evaluate BODY with display restricted to the currently shown -message." - `(save-excursion - (save-restriction - (let ((extent (notmuch-show-message-extent))) - (narrow-to-region (car extent) (cdr extent)) - ,@body)))) - -(defun notmuch-show--gather-urls () - "Gather any URLs in the current message." - (notmuch-show--with-currently-shown-message - (let (urls) - (goto-char (point-min)) - (while (re-search-forward goto-address-url-regexp (point-max) t) - (push (match-string-no-properties 0) urls)) - (reverse urls)))) - -(defun notmuch-show-browse-urls (&optional kill) - "Offer to browse any URLs in the current message. -With a prefix argument, copy the URL to the kill ring rather than -browsing." - (interactive "P") - (let ((urls (notmuch-show--gather-urls)) - (prompt (if kill "Copy URL to kill ring: " "Browse URL: ")) - (fn (if kill #'kill-new #'browse-url))) - (if urls - (funcall fn (completing-read prompt urls nil nil nil nil (car urls))) - (message "No URLs found.")))) - -;;; _ - -(provide 'notmuch-show) - -;;; notmuch-show.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-show.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-show.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-tag.el b/emacs/elpa/notmuch-20231006.2337/notmuch-tag.el @@ -1,587 +0,0 @@ -;;; notmuch-tag.el --- tag messages within emacs -*- lexical-binding: t -*- -;; -;; Copyright © Damien Cassou -;; Copyright © Carl Worth -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: Carl Worth <cworth@cworth.org> -;; Damien Cassou <damien.cassou@gmail.com> - -;;; Code: - -(require 'crm) - -(require 'notmuch-lib) - -(declare-function notmuch-search-tag "notmuch" - (tag-changes &optional beg end only-matched)) -(declare-function notmuch-show-tag "notmuch-show" (tag-changes)) -(declare-function notmuch-tree-tag "notmuch-tree" (tag-changes)) -(declare-function notmuch-jump "notmuch-jump" (action-map prompt)) - -;;; Keys - -(define-widget 'notmuch-tag-key-type 'list - "A single key tagging binding." - :format "%v" - :args '((list :inline t - :format "%v" - (key-sequence :tag "Key") - (radio :tag "Tag operations" - (repeat :tag "Tag list" - (string :format "%v" :tag "change")) - (variable :tag "Tag variable")) - (string :tag "Name")))) - -(defcustom notmuch-tagging-keys - `((,(kbd "a") notmuch-archive-tags "Archive") - (,(kbd "u") notmuch-show-mark-read-tags "Mark read") - (,(kbd "f") ("+flagged") "Flag") - (,(kbd "s") ("+spam" "-inbox") "Mark as spam") - (,(kbd "d") ("+deleted" "-inbox") "Delete")) - "A list of keys and corresponding tagging operations. - -For each key (or key sequence) you can specify a sequence of -tagging operations to apply, or a variable which contains a list -of tagging operations such as `notmuch-archive-tags'. The final -element is a name for this tagging operation. If the name is -omitted or empty then the list of tag changes, or the variable -name is used as the name. - -The key `notmuch-tag-jump-reverse-key' (k by default) should not -be used (either as a key, or as the start of a key sequence) as -it is already bound: it switches the menu to a menu of the -reverse tagging operations. The reverse of a tagging operation is -the same list of individual tag-ops but with `+tag' replaced by -`-tag' and vice versa. - -If setting this variable outside of customize then it should be a -list of triples (lists of three elements). Each triple should be -of the form (key-binding tagging-operations name). KEY-BINDING -can be a single character or a key sequence; TAGGING-OPERATIONS -should either be a list of individual tag operations each of the -form `+tag' or `-tag', or the variable name of a variable that is -a list of tagging operations; NAME should be a name for the -tagging operation, if omitted or empty than then name is taken -from TAGGING-OPERATIONS." - :tag "List of tagging bindings" - :type '(repeat notmuch-tag-key-type) - :group 'notmuch-tag) - -;;; Faces and Formats - -(define-widget 'notmuch-tag-format-type 'lazy - "Customize widget for notmuch-tag-format and friends." - :type '(alist :key-type (regexp :tag "Tag") - :extra-offset -3 - :value-type - (radio :format "%v" - (const :tag "Hidden" nil) - (set :tag "Modified" - (string :tag "Display as") - (list :tag "Face" :extra-offset -4 - (const :format "" :inline t - (notmuch-apply-face tag)) - (list :format "%v" - (const :format "" quote) - custom-face-edit)) - (list :format "%v" :extra-offset -4 - (const :format "" :inline t - (notmuch-tag-format-image-data tag)) - (choice :tag "Image" - (const :tag "Star" - (notmuch-tag-star-icon)) - (const :tag "Empty star" - (notmuch-tag-star-empty-icon)) - (const :tag "Tag" - (notmuch-tag-tag-icon)) - (string :tag "Custom"))) - (sexp :tag "Custom"))))) - -(defface notmuch-tag-unread - '((t :foreground "red")) - "Default face used for the unread tag. - -Used in the default value of `notmuch-tag-formats'." - :group 'notmuch-faces) - -(defface notmuch-tag-flagged - '((((class color) - (background dark)) - (:foreground "LightBlue1")) - (((class color) - (background light)) - (:foreground "blue"))) - "Face used for the flagged tag. - -Used in the default value of `notmuch-tag-formats'." - :group 'notmuch-faces) - -(defcustom notmuch-tag-formats - '(("unread" (propertize tag 'face 'notmuch-tag-unread)) - ("flagged" (propertize tag 'face 'notmuch-tag-flagged) - (notmuch-tag-format-image-data tag (notmuch-tag-star-icon)))) - "Custom formats for individual tags. - -This is an association list of the form ((MATCH EXPR...)...), -mapping tag name regexps to lists of formatting expressions. - -The first entry whose MATCH regexp-matches a tag is used to -format that tag. The regexp is implicitly anchored, so to match -a literal tag name, just use that tag name (if it contains -special regexp characters like \".\" or \"*\", these have to be -escaped). - -The cdr of the matching entry gives a list of Elisp expressions -that modify the tag. If the list is empty, the tag is simply -hidden. Otherwise, each expression EXPR is evaluated in order: -for the first expression, the variable `tag' is bound to the tag -name; for each later expression, the variable `tag' is bound to -the result of the previous expression. In this way, each -expression can build on the formatting performed by the previous -expression. The result of the last expression is displayed in -place of the tag. - -For example, to replace a tag with another string, simply use -that string as a formatting expression. To change the foreground -of a tag to red, use the expression - (propertize tag \\='face \\='(:foreground \"red\")) - -See also `notmuch-tag-format-image', which can help replace tags -with images." - :group 'notmuch-search - :group 'notmuch-show - :group 'notmuch-faces - :type 'notmuch-tag-format-type) - -(defface notmuch-tag-deleted - '((((class color) (supports :strike-through "red")) :strike-through "red") - (t :inverse-video t)) - "Face used to display deleted tags. - -Used in the default value of `notmuch-tag-deleted-formats'." - :group 'notmuch-faces) - -(defcustom notmuch-tag-deleted-formats - '(("unread" (notmuch-apply-face bare-tag `notmuch-tag-deleted)) - (".*" (notmuch-apply-face tag `notmuch-tag-deleted))) - "Custom formats for tags when deleted. - -For deleted tags the formats in `notmuch-tag-formats' are applied -first and then these formats are applied on top; that is `tag' -passed to the function is the tag with all these previous -formattings applied. The formatted can access the original -unformatted tag as `bare-tag'. - -By default this shows deleted tags with strike-through in red, -unless strike-through is not available (e.g., emacs is running in -a terminal) in which case it uses inverse video. To hide deleted -tags completely set this to - \\='((\".*\" nil)) - -See `notmuch-tag-formats' for full documentation." - :group 'notmuch-show - :group 'notmuch-faces - :type 'notmuch-tag-format-type) - -(defface notmuch-tag-added - '((t :underline "green")) - "Default face used for added tags. - -Used in the default value for `notmuch-tag-added-formats'." - :group 'notmuch-faces) - -(defcustom notmuch-tag-added-formats - '((".*" (notmuch-apply-face tag 'notmuch-tag-added))) - "Custom formats for tags when added. - -For added tags the formats in `notmuch-tag-formats' are applied -first and then these formats are applied on top. - -To disable special formatting of added tags, set this variable to -nil. - -See `notmuch-tag-formats' for full documentation." - :group 'notmuch-show - :group 'notmuch-faces - :type 'notmuch-tag-format-type) - -;;; Icons - -(defun notmuch-tag-format-image-data (tag data) - "Replace TAG with image DATA, if available. - -This function returns a propertized string that will display image -DATA in place of TAG.This is designed for use in -`notmuch-tag-formats'. - -DATA is the content of an SVG picture (e.g., as returned by -`notmuch-tag-star-icon')." - (propertize tag 'display - `(image :type svg - :data ,data - :ascent center - :mask heuristic))) - -(defun notmuch-tag-star-icon () - "Return SVG data representing a star icon. -This can be used with `notmuch-tag-format-image-data'." - "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?> -<svg version=\"1.1\" width=\"16\" height=\"16\" xmlns=\"http://www.w3.org/2000/svg\"> - <g transform=\"translate(-242.81601,-315.59635)\"> - <path - d=\"m 290.25762,334.31206 -17.64143,-11.77975 -19.70508,7.85447 5.75171,-20.41814 -13.55925,-16.31348 21.19618,-0.83936 11.325,-17.93675 7.34825,19.89939 20.55849,5.22795 -16.65471,13.13786 z\" - transform=\"matrix(0.2484147,-0.02623394,0.02623394,0.2484147,174.63605,255.37691)\" - style=\"fill:#ffff00;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1\" /> - </g> -</svg>") - -(defun notmuch-tag-star-empty-icon () - "Return SVG data representing an empty star icon. -This can be used with `notmuch-tag-format-image-data'." - "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?> -<svg version=\"1.1\" width=\"16\" height=\"16\" xmlns=\"http://www.w3.org/2000/svg\"> - <g transform=\"translate(-242.81601,-315.59635)\"> - <path - d=\"m 290.25762,334.31206 -17.64143,-11.77975 -19.70508,7.85447 5.75171,-20.41814 -13.55925,-16.31348 21.19618,-0.83936 11.325,-17.93675 7.34825,19.89939 20.55849,5.22795 -16.65471,13.13786 z\" - transform=\"matrix(0.2484147,-0.02623394,0.02623394,0.2484147,174.63605,255.37691)\" - style=\"fill:#d6d6d1;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1\" /> - </g> -</svg>") - -(defun notmuch-tag-tag-icon () - "Return SVG data representing a tag icon. -This can be used with `notmuch-tag-format-image-data'." - "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?> -<svg version=\"1.1\" width=\"16\" height=\"16\" xmlns=\"http://www.w3.org/2000/svg\"> - <g transform=\"translate(0,-1036.3622)\"> - <path - d=\"m 0.44642857,1040.9336 12.50000043,0 2.700893,3.6161 -2.700893,3.616 -12.50000043,0 z\" - style=\"fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1\" /> - </g> -</svg>") - -;;; track history of tag operations -(defvar-local notmuch-tag-history nil - "Buffer local history of `notmuch-tag' function.") -(put 'notmuch-tag-history 'permanent-local t) - -;;; Format Handling - -(defvar notmuch-tag--format-cache (make-hash-table :test 'equal) - "Cache of tag format lookup. Internal to `notmuch-tag-format-tag'.") - -(defun notmuch-tag-clear-cache () - "Clear the internal cache of tag formats." - (clrhash notmuch-tag--format-cache)) - -(defun notmuch-tag--get-formats (tag alist) - "Find the first item whose car regexp-matches TAG." - (save-match-data - ;; Don't use assoc-default since there's no way to distinguish a - ;; missing key from a present key with a null cdr. - (cl-assoc tag alist - :test (lambda (tag key) - (and (eq (string-match key tag) 0) - (= (match-end 0) (length tag))))))) - -(defun notmuch-tag--do-format (bare-tag tag formats) - "Apply a tag-formats entry to TAG." - (cond ((null formats) ;; - Tag not in `formats', - tag) ;; the format is the tag itself. - ((null (cdr formats)) ;; - Tag was deliberately hidden, - nil) ;; no format must be returned - (t - ;; Tag was found and has formats, we must apply all the - ;; formats. TAG may be null so treat that as a special case. - (let ((return-tag (copy-sequence (or tag "")))) - (dolist (format (cdr formats)) - (setq return-tag - (eval format - `((bare-tag . ,bare-tag) - (tag . ,return-tag))))) - (if (and (null tag) (equal return-tag "")) - nil - return-tag))))) - -(defun notmuch-tag-format-tag (tags orig-tags tag) - "Format TAG according to `notmuch-tag-formats'. - -TAGS and ORIG-TAGS are lists of the current tags and the original -tags; tags which have been deleted (i.e., are in ORIG-TAGS but -are not in TAGS) are shown using formats from -`notmuch-tag-deleted-formats'; tags which have been added (i.e., -are in TAGS but are not in ORIG-TAGS) are shown using formats -from `notmuch-tag-added-formats' and tags which have not been -changed (the normal case) are shown using formats from -`notmuch-tag-formats'." - (let* ((tag-state (cond ((not (member tag tags)) 'deleted) - ((not (member tag orig-tags)) 'added))) - (formatted-tag (gethash (cons tag tag-state) - notmuch-tag--format-cache - 'missing))) - (when (eq formatted-tag 'missing) - (let ((base (notmuch-tag--get-formats tag notmuch-tag-formats)) - (over (cl-case tag-state - (deleted (notmuch-tag--get-formats - tag notmuch-tag-deleted-formats)) - (added (notmuch-tag--get-formats - tag notmuch-tag-added-formats)) - (otherwise nil)))) - (setq formatted-tag (notmuch-tag--do-format tag tag base)) - (setq formatted-tag (notmuch-tag--do-format tag formatted-tag over)) - (puthash (cons tag tag-state) formatted-tag notmuch-tag--format-cache))) - formatted-tag)) - -(defun notmuch-tag-format-tags (tags orig-tags &optional face) - "Return a string representing formatted TAGS." - (let ((face (or face 'notmuch-tag-face)) - (all-tags (sort (delete-dups (append tags orig-tags nil)) #'string<))) - (notmuch-apply-face - (mapconcat #'identity - ;; nil indicated that the tag was deliberately hidden - (delq nil (mapcar (apply-partially #'notmuch-tag-format-tag - tags orig-tags) - all-tags)) - " ") - face - t))) - -;;; Hooks - -(defcustom notmuch-before-tag-hook nil - "Hooks that are run before tags of a message are modified. - -`tag-changes' will contain the tags that are about to be added or removed as -a list of strings of the form \"+TAG\" or \"-TAG\". -`query' will be a string containing the search query that determines -the messages that are about to be tagged." - :type 'hook - :options '(notmuch-hl-line-mode) - :group 'notmuch-hooks) - -(defcustom notmuch-after-tag-hook nil - "Hooks that are run after tags of a message are modified. - -`tag-changes' will contain the tags that were added or removed as -a list of strings of the form \"+TAG\" or \"-TAG\". -`query' will be a string containing the search query that determines -the messages that were tagged." - :type 'hook - :options '(notmuch-hl-line-mode) - :group 'notmuch-hooks) - -;;; User Input - -(defvar notmuch-select-tag-history nil - "Minibuffer history of `notmuch-select-tag-with-completion' function.") - -(defvar notmuch-read-tag-changes-history nil - "Minibuffer history of `notmuch-read-tag-changes' function.") - -(defun notmuch-tag-completions (&rest search-terms) - "Return a list of tags for messages matching SEARCH-TERMS. - -Return all tags if no search terms are given." - (unless search-terms - (setq search-terms (list "*"))) - (split-string - (with-output-to-string - (with-current-buffer standard-output - (apply 'notmuch--call-process notmuch-command nil t - nil "search" "--output=tags" "--exclude=false" search-terms))) - "\n+" t)) - -(defun notmuch-select-tag-with-completion (prompt &rest search-terms) - (completing-read prompt - (apply #'notmuch-tag-completions search-terms) - nil nil nil 'notmuch-select-tag-history)) - -(defun notmuch-read-tag-changes (current-tags &optional prompt initial-input) - "Prompt for tag changes in the minibuffer. - -CURRENT-TAGS is a list of tags that are present on the message -or messages to be changed. These are offered as tag removal -completions. CURRENT-TAGS may contain duplicates. PROMPT, if -non-nil, is the query string to present in the minibuffer. It -defaults to \"Tags\". INITIAL-INPUT, if non-nil, will be the -initial input in the minibuffer." - (let* ((all-tag-list (notmuch-tag-completions)) - (add-tag-list (mapcar (apply-partially 'concat "+") all-tag-list)) - (remove-tag-list (mapcar (apply-partially 'concat "-") current-tags)) - (tag-list (append add-tag-list remove-tag-list)) - (prompt (concat (or prompt "Tags") " (+add -drop): ")) - (crm-separator " ") - ;; By default, space is bound to "complete word" function. - ;; Re-bind it to insert a space instead. Note that <tab> - ;; still does the completion. - (crm-local-completion-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map crm-local-completion-map) - (define-key map " " 'self-insert-command) - map))) - (completing-read-multiple prompt tag-list - nil nil initial-input - 'notmuch-read-tag-changes-history))) - -;;; Tagging - -(defun notmuch-update-tags (tags tag-changes) - "Return a copy of TAGS with additions and removals from TAG-CHANGES. - -TAG-CHANGES must be a list of tags names, each prefixed with -either a \"+\" to indicate the tag should be added to TAGS if not -present or a \"-\" to indicate that the tag should be removed -from TAGS if present." - (let ((result-tags (copy-sequence tags))) - (dolist (tag-change tag-changes) - (let ((tag (and (not (string-empty-p tag-change)) - (substring tag-change 1)))) - (cl-case (aref tag-change 0) - (?+ (unless (member tag result-tags) - (push tag result-tags))) - (?- (setq result-tags (delete tag result-tags))) - (otherwise - (error "Changed tag must be of the form `+this_tag' or `-that_tag'"))))) - (sort result-tags 'string<))) - -(defconst notmuch-tag-argument-limit 1000 - "Use batch tagging if the tagging query is longer than this. - -This limits the length of arguments passed to the notmuch CLI to -avoid system argument length limits and performance problems. - -NOTE: this variable is no longer used.") - -(make-obsolete-variable 'notmuch-tag-argument-limit nil "notmuch 0.36") - -(defun notmuch-tag (query tag-changes &optional omit-hist) - "Add/remove tags in TAG-CHANGES to messages matching QUERY. - -QUERY should be a string containing the search-terms. -TAG-CHANGES is a list of strings of the form \"+tag\" or \"-tag\" -to add or remove tags, respectively. OMIT-HIST disables history -tracking if non-nil. - -Note: Other code should always use this function to alter tags of -messages instead of running (notmuch-call-notmuch-process \"tag\" ..) -directly, so that hooks specified in notmuch-before-tag-hook and -notmuch-after-tag-hook will be run." - ;; Perform some validation - (dolist (tag-change tag-changes) - (unless (string-match-p "^[-+]\\S-+$" tag-change) - (error "Tag must be of the form `+this_tag' or `-that_tag'"))) - (unless query - (error "Nothing to tag!")) - (when tag-changes - (notmuch-dlet ((tag-changes tag-changes) - (query query)) - (run-hooks 'notmuch-before-tag-hook)) - (with-temp-buffer - (insert (concat (mapconcat #'notmuch-hex-encode tag-changes " ") " -- " query)) - (unless (= 0 - (notmuch--call-process-region - (point-min) (point-max) notmuch-command t t nil "tag" "--batch")) - (notmuch-logged-error "notmuch tag failed" (buffer-string)))) - (unless omit-hist - (push (list :query query :tag-changes tag-changes) notmuch-tag-history))) - (notmuch-dlet ((tag-changes tag-changes) - (query query)) - (run-hooks 'notmuch-after-tag-hook))) - -(defun notmuch-tag-undo () - "Undo the previous tagging operation in the current buffer. Uses -buffer local variable `notmuch-tag-history' to determine what -that operation was." - (interactive) - (when (null notmuch-tag-history) - (error "no further notmuch undo information")) - (let* ((action (pop notmuch-tag-history)) - (query (plist-get action :query)) - (changes (notmuch-tag-change-list (plist-get action :tag-changes) t))) - (notmuch-tag query changes t)) - (notmuch-refresh-this-buffer)) - -(defun notmuch-tag-change-list (tags &optional reverse) - "Convert TAGS into a list of tag changes. - -Add a \"+\" prefix to any tag in TAGS list that doesn't already -begin with a \"+\" or a \"-\". If REVERSE is non-nil, replace all -\"+\" prefixes with \"-\" and vice versa in the result." - (mapcar (lambda (str) - (let ((s (if (string-match "^[+-]" str) str (concat "+" str)))) - (if reverse - (concat (if (= (string-to-char s) ?-) "+" "-") - (substring s 1)) - s))) - tags)) - -(defvar notmuch-tag-jump-reverse-key "k" - "The key in tag-jump to switch to the reverse tag changes.") - -(defun notmuch-tag-jump (reverse) - "Create a jump menu for tagging operations. - -Creates and displays a jump menu for the tagging operations -specified in `notmuch-tagging-keys'. If REVERSE is set then it -offers a menu of the reverses of the operations specified in -`notmuch-tagging-keys'; i.e. each `+tag' is replaced by `-tag' -and vice versa." - ;; In principle this function is simple, but it has to deal with - ;; lots of cases: different modes (search/show/tree), whether a name - ;; is specified, whether the tagging operations is a list of - ;; tag-ops, or a symbol that evaluates to such a list, and whether - ;; REVERSE is specified. - (interactive "P") - (let (action-map) - (pcase-dolist (`(,key ,tag ,name) notmuch-tagging-keys) - (let* ((tag-function (cl-case major-mode - (notmuch-search-mode #'notmuch-search-tag) - (notmuch-show-mode #'notmuch-show-tag) - (notmuch-tree-mode #'notmuch-tree-tag))) - (tag (if (symbolp tag) - (symbol-value tag) - tag)) - (tag-change (if reverse - (notmuch-tag-change-list tag t) - tag)) - (name (or (and (not (string= name "")) - name) - (and (symbolp name) - (symbol-name name)))) - (name-string (if name - (if reverse - (concat "Reverse " name) - name) - (mapconcat #'identity tag-change " ")))) - (push (list key name-string - (lambda () (funcall tag-function tag-change))) - action-map))) - (push (list notmuch-tag-jump-reverse-key - (if reverse - "Forward tag changes " - "Reverse tag changes") - (apply-partially 'notmuch-tag-jump (not reverse))) - action-map) - (setq action-map (nreverse action-map)) - (notmuch-jump action-map "Tag: "))) - -;;; _ - -(provide 'notmuch-tag) - -;;; notmuch-tag.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-tag.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-tag.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-tree.el b/emacs/elpa/notmuch-20231006.2337/notmuch-tree.el @@ -1,1467 +0,0 @@ -;;; notmuch-tree.el --- displaying notmuch forests -*- lexical-binding: t -*- -;; -;; Copyright © Carl Worth -;; Copyright © David Edmondson -;; Copyright © Mark Walters -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: David Edmondson <dme@dme.org> -;; Mark Walters <markwalters1009@gmail.com> - -;;; Code: - -(require 'mail-parse) - -(require 'notmuch-lib) -(require 'notmuch-show) -(require 'notmuch-tag) -(require 'notmuch-parser) -(require 'notmuch-jump) - -(declare-function notmuch-search "notmuch" - (&optional query oldest-first target-thread target-line - no-display)) -(declare-function notmuch-call-notmuch-process "notmuch-lib" (&rest args)) -(declare-function notmuch-read-query "notmuch" (prompt)) -(declare-function notmuch-search-find-thread-id "notmuch" (&optional bare)) -(declare-function notmuch-search-find-subject "notmuch" ()) - -;; For `notmuch-tree-next-thread-from-search'. -(declare-function notmuch-search-next-thread "notmuch" ()) -(declare-function notmuch-search-previous-thread "notmuch" ()) -(declare-function notmuch-tree-from-search-thread "notmuch" ()) - -;; this variable distinguishes the unthreaded display from the normal tree display -(defvar-local notmuch-tree-unthreaded nil - "A buffer local copy of argument unthreaded to the function notmuch-tree.") - -;;; Options - -(defgroup notmuch-tree nil - "Showing message and thread structure." - :group 'notmuch) - -(defcustom notmuch-tree-show-out nil - "View selected messages in new window rather than split-pane." - :type 'boolean - :group 'notmuch-tree) - -(defcustom notmuch-unthreaded-show-out t - "View selected messages in new window rather than split-pane." - :type 'boolean - :group 'notmuch-tree) - -(defun notmuch-tree-show-out () - (if notmuch-tree-unthreaded - notmuch-unthreaded-show-out - notmuch-tree-show-out)) - -(defcustom notmuch-tree-thread-symbols - '((prefix . " ") - (top . "─") - (top-tee . "┬") - (vertical . "│") - (vertical-tee . "├") - (bottom . "╰") - (arrow . "►")) - "Strings used to draw trees in notmuch tree results. -Symbol keys denote where the corresponding string value is used: -`prefix' is used at the top of the tree, followed by `top' if it -has no children or `top-tee' if it does; `vertical' is a bar -connecting with a response down the list skipping the current -one, while `vertical-tee' marks the current message as a reply to -the previous one; `bottom' is used at the bottom of threads. -Finally, the `arrrow' string in the list is used as a pointer to -every message. - -Common customizations include setting `prefix' to \"-\", to see -equal-length prefixes, and `arrow' to an empty string or to a -different kind of arrow point." - :type '(alist :key-type symbol :value-type string) - :group 'notmuch-tree) - -(defconst notmuch-tree--field-names - '(choice :tag "Field" - (const :tag "Date" "date") - (const :tag "Authors" "authors") - (const :tag "Subject" "subject") - (const :tag "Tree" "tree") - (const :tag "Tags" "tags") - (function))) - -(defcustom notmuch-tree-result-format - `(("date" . "%12s ") - ("authors" . "%-20s") - ((("tree" . "%s") - ("subject" . "%s")) - . " %-54s ") - ("tags" . "(%s)")) - "Result formatting for tree view. - -List of pairs of (field . format-string). Supported field -strings are: \"date\", \"authors\", \"subject\", \"tree\", -\"tags\". It is also supported to pass a function in place of a -field-name. In this case the function is passed the thread -object (plist) and format string. - -Tree means the thread tree box graphics. The field may -also be a list in which case the formatting rules are -applied recursively and then the output of all the fields -in the list is inserted according to format-string. - -Note that the author string should not contain whitespace -\(put it in the neighbouring fields instead)." - - :type `(alist :key-type (choice ,notmuch-tree--field-names - (alist :key-type ,notmuch-tree--field-names - :value-type (string :tag "Format"))) - :value-type (string :tag "Format")) - :group 'notmuch-tree) - -(defcustom notmuch-unthreaded-result-format - `(("date" . "%12s ") - ("authors" . "%-20s") - ((("subject" . "%s")) ." %-54s ") - ("tags" . "(%s)")) - "Result formatting for unthreaded tree view. - -List of pairs of (field . format-string). Supported field -strings are: \"date\", \"authors\", \"subject\", \"tree\", -\"tags\". It is also supported to pass a function in place of a -field-name. In this case the function is passed the thread -object (plist) and format string. - -Tree means the thread tree box graphics. The field may -also be a list in which case the formatting rules are -applied recursively and then the output of all the fields -in the list is inserted according to format-string. - -Note that the author string should not contain whitespace -\(put it in the neighbouring fields instead)." - - :type `(alist :key-type (choice ,notmuch-tree--field-names - (alist :key-type ,notmuch-tree--field-names - :value-type (string :tag "Format"))) - :value-type (string :tag "Format")) - :group 'notmuch-tree) - -(defun notmuch-tree-result-format () - (if notmuch-tree-unthreaded - notmuch-unthreaded-result-format - notmuch-tree-result-format)) - -;;; Faces -;;;; Faces for messages that match the query - -(defface notmuch-tree-match-face - '((t :inherit default)) - "Default face used in tree mode face for matching messages" - :group 'notmuch-tree - :group 'notmuch-faces) - -(defface notmuch-tree-match-date-face - nil - "Face used in tree mode for the date in messages matching the query." - :group 'notmuch-tree - :group 'notmuch-faces) - -(defface notmuch-tree-match-author-face - '((((class color) - (background dark)) - (:foreground "OliveDrab1")) - (((class color) - (background light)) - (:foreground "dark blue")) - (t - (:bold t))) - "Face used in tree mode for the author in messages matching the query." - :group 'notmuch-tree - :group 'notmuch-faces) - -(defface notmuch-tree-match-subject-face - nil - "Face used in tree mode for the subject in messages matching the query." - :group 'notmuch-tree - :group 'notmuch-faces) - -(defface notmuch-tree-match-tree-face - nil - "Face used in tree mode for the thread tree block graphics in -messages matching the query." - :group 'notmuch-tree - :group 'notmuch-faces) - -(defface notmuch-tree-match-tag-face - '((((class color) - (background dark)) - (:foreground "OliveDrab1")) - (((class color) - (background light)) - (:foreground "navy blue" :bold t)) - (t - (:bold t))) - "Face used in tree mode for tags in messages matching the query." - :group 'notmuch-tree - :group 'notmuch-faces) - -;;;; Faces for messages that do not match the query - -(defface notmuch-tree-no-match-face - '((t (:foreground "gray"))) - "Default face used in tree mode face for non-matching messages." - :group 'notmuch-tree - :group 'notmuch-faces) - -(defface notmuch-tree-no-match-date-face - nil - "Face used in tree mode for non-matching dates." - :group 'notmuch-tree - :group 'notmuch-faces) - -(defface notmuch-tree-no-match-subject-face - nil - "Face used in tree mode for non-matching subjects." - :group 'notmuch-tree - :group 'notmuch-faces) - -(defface notmuch-tree-no-match-tree-face - nil - "Face used in tree mode for the thread tree block graphics in -messages matching the query." - :group 'notmuch-tree - :group 'notmuch-faces) - -(defface notmuch-tree-no-match-author-face - nil - "Face used in tree mode for non-matching authors." - :group 'notmuch-tree - :group 'notmuch-faces) - -(defface notmuch-tree-no-match-tag-face - nil - "Face used in tree mode face for non-matching tags." - :group 'notmuch-tree - :group 'notmuch-faces) - -;;; Variables - -(defvar-local notmuch-tree-previous-subject - "The subject of the most recent result shown during the async display.") - -(defvar-local notmuch-tree-basic-query nil - "A buffer local copy of argument query to the function notmuch-tree.") - -(defvar-local notmuch-tree-query-context nil - "A buffer local copy of argument query-context to the function notmuch-tree.") - -(defvar-local notmuch-tree-target-msg nil - "A buffer local copy of argument target to the function notmuch-tree.") - -(defvar-local notmuch-tree-open-target nil - "A buffer local copy of argument open-target to the function notmuch-tree.") - -(defvar-local notmuch-tree-parent-buffer nil) - -(defvar-local notmuch-tree-message-window nil - "The window of the message pane. - -It is set in both the tree buffer and the child show buffer. It -is used to try and close the message pane when quitting tree view -or the child show buffer.") -(put 'notmuch-tree-message-window 'permanent-local t) - -(defvar-local notmuch-tree-message-buffer nil - "The buffer name of the show buffer in the message pane. - -This is used to try and make sure we don't close the message pane -if the user has loaded a different buffer in that window.") -(put 'notmuch-tree-message-buffer 'permanent-local t) - -;;; Tree wrapper commands - -(defmacro notmuch-tree--define-do-in-message-window (name cmd) - "Define NAME as a command that calls CMD interactively in the message window. -If the message pane is closed then this command does nothing. -Avoid using this macro in new code; it will be removed." - `(defun ,name () - ,(concat "(In message window) " (documentation cmd t)) - (interactive) - (when (window-live-p notmuch-tree-message-window) - (with-selected-window notmuch-tree-message-window - (call-interactively #',cmd))))) - -(notmuch-tree--define-do-in-message-window - notmuch-tree-previous-message-button - notmuch-show-previous-button) -(notmuch-tree--define-do-in-message-window - notmuch-tree-next-message-button - notmuch-show-next-button) -(notmuch-tree--define-do-in-message-window - notmuch-tree-toggle-message-process-crypto - notmuch-show-toggle-process-crypto) - -(defun notmuch-tree--message-process-crypto () - "Return value of `notmuch-show-process-crypto' in the message window. -If that window isn't alive, then return the current value. -Avoid using this function in new code; it will be removed." - (if (window-live-p notmuch-tree-message-window) - (with-selected-window notmuch-tree-message-window - notmuch-show-process-crypto) - notmuch-show-process-crypto)) - -(defmacro notmuch-tree--define-close-message-window-and (name cmd) - "Define NAME as a variant of CMD. - -NAME determines the value of `notmuch-show-process-crypto' in the -message window, closes the window, and then call CMD interactively -with that value let-bound. If the message window does not exist, -then NAME behaves like CMD." - `(defun ,name () - ,(concat "(Close message pane and) " (documentation cmd t)) - (interactive) - (let ((notmuch-show-process-crypto - (notmuch-tree--message-process-crypto))) - (notmuch-tree-close-message-window) - (call-interactively #',cmd)))) - -(notmuch-tree--define-close-message-window-and - notmuch-tree-help - notmuch-help) -(notmuch-tree--define-close-message-window-and - notmuch-tree-new-mail - notmuch-mua-new-mail) -(notmuch-tree--define-close-message-window-and - notmuch-tree-jump-search - notmuch-jump-search) -(notmuch-tree--define-close-message-window-and - notmuch-tree-forward-message - notmuch-show-forward-message) -(notmuch-tree--define-close-message-window-and - notmuch-tree-reply-sender - notmuch-show-reply-sender) -(notmuch-tree--define-close-message-window-and - notmuch-tree-reply - notmuch-show-reply) -(notmuch-tree--define-close-message-window-and - notmuch-tree-view-raw-message - notmuch-show-view-raw-message) - -;;; Keymap - -(defvar notmuch-tree-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map notmuch-common-keymap) - ;; These bindings shadow common bindings with variants - ;; that additionally close the message window. - (define-key map [remap notmuch-bury-or-kill-this-buffer] 'notmuch-tree-quit) - (define-key map [remap notmuch-search] 'notmuch-tree-to-search) - (define-key map [remap notmuch-help] 'notmuch-tree-help) - (define-key map [remap notmuch-mua-new-mail] 'notmuch-tree-new-mail) - (define-key map [remap notmuch-jump-search] 'notmuch-tree-jump-search) - - (define-key map "o" 'notmuch-tree-toggle-order) - (define-key map "S" 'notmuch-search-from-tree-current-query) - (define-key map "U" 'notmuch-unthreaded-from-tree-current-query) - (define-key map "Z" 'notmuch-tree-from-unthreaded-current-query) - - ;; these use notmuch-show functions directly - (define-key map "|" 'notmuch-show-pipe-message) - (define-key map "w" 'notmuch-show-save-attachments) - (define-key map "v" 'notmuch-show-view-all-mime-parts) - (define-key map "c" 'notmuch-show-stash-map) - (define-key map "b" 'notmuch-show-resend-message) - - ;; these apply to the message pane - (define-key map (kbd "M-TAB") 'notmuch-tree-previous-message-button) - (define-key map (kbd "<backtab>") 'notmuch-tree-previous-message-button) - (define-key map (kbd "TAB") 'notmuch-tree-next-message-button) - (define-key map "$" 'notmuch-tree-toggle-message-process-crypto) - - ;; bindings from show (or elsewhere) but we close the message pane first. - (define-key map "f" 'notmuch-tree-forward-message) - (define-key map "r" 'notmuch-tree-reply-sender) - (define-key map "R" 'notmuch-tree-reply) - (define-key map "V" 'notmuch-tree-view-raw-message) - (define-key map "l" 'notmuch-tree-filter) - (define-key map "t" 'notmuch-tree-filter-by-tag) - (define-key map "E" 'notmuch-tree-edit-search) - - ;; The main tree view bindings - (define-key map (kbd "RET") 'notmuch-tree-show-message) - (define-key map [mouse-1] 'notmuch-tree-show-message) - (define-key map "x" 'notmuch-tree-archive-message-then-next-or-exit) - (define-key map "X" 'notmuch-tree-archive-thread-then-exit) - (define-key map "A" 'notmuch-tree-archive-thread-then-next) - (define-key map "a" 'notmuch-tree-archive-message-then-next) - (define-key map "z" 'notmuch-tree-to-tree) - (define-key map "n" 'notmuch-tree-next-matching-message) - (define-key map "p" 'notmuch-tree-prev-matching-message) - (define-key map "N" 'notmuch-tree-next-message) - (define-key map "P" 'notmuch-tree-prev-message) - (define-key map (kbd "M-p") 'notmuch-tree-prev-thread) - (define-key map (kbd "M-n") 'notmuch-tree-next-thread) - (define-key map "k" 'notmuch-tag-jump) - (define-key map "-" 'notmuch-tree-remove-tag) - (define-key map "+" 'notmuch-tree-add-tag) - (define-key map "*" 'notmuch-tree-tag-thread) - (define-key map " " 'notmuch-tree-scroll-or-next) - (define-key map (kbd "DEL") 'notmuch-tree-scroll-message-window-back) - (define-key map "e" 'notmuch-tree-resume-message) - map) - "Keymap for \"notmuch tree\" buffers.") - -;;; Message properties - -(defun notmuch-tree-get-message-properties () - "Return the properties of the current message as a plist. - -Some useful entries are: -:headers - Property list containing the headers :Date, :Subject, :From, etc. -:tags - Tags for this message." - (save-excursion - (beginning-of-line) - (get-text-property (point) :notmuch-message-properties))) - -(defun notmuch-tree-set-message-properties (props) - (save-excursion - (beginning-of-line) - (put-text-property (point) - (+ (point) 1) - :notmuch-message-properties props))) - -(defun notmuch-tree-set-prop (prop val &optional props) - (let ((inhibit-read-only t) - (props (or props - (notmuch-tree-get-message-properties)))) - (plist-put props prop val) - (notmuch-tree-set-message-properties props))) - -(defun notmuch-tree-get-prop (prop &optional props) - (plist-get (or props (notmuch-tree-get-message-properties)) - prop)) - -(defun notmuch-tree-set-tags (tags) - "Set the tags of the current message." - (notmuch-tree-set-prop :tags tags)) - -(defun notmuch-tree-get-tags () - "Return the tags of the current message." - (notmuch-tree-get-prop :tags)) - -(defun notmuch-tree-get-message-id (&optional bare) - "Return the message id of the current message." - (let ((id (notmuch-tree-get-prop :id))) - (if id - (if bare - id - (notmuch-id-to-query id)) - nil))) - -(defun notmuch-tree-get-match () - "Return whether the current message is a match." - (notmuch-tree-get-prop :match)) - -;;; Update display - -(defun notmuch-tree-refresh-result () - "Redisplay the current message line. - -This redisplays the current line based on the messages -properties (as they are now). This is used when tags are -updated." - (let ((init-point (point)) - (end (line-end-position)) - (msg (notmuch-tree-get-message-properties)) - (inhibit-read-only t)) - (beginning-of-line) - ;; This is a little tricky: we override - ;; notmuch-tree-previous-subject to get the decision between - ;; ... and a subject right and it stops notmuch-tree-insert-msg - ;; from overwriting the buffer local copy of - ;; notmuch-tree-previous-subject if this is called while the - ;; buffer is displaying. - (let ((notmuch-tree-previous-subject - (notmuch-tree-get-prop :previous-subject))) - (delete-region (point) (1+ (line-end-position))) - (notmuch-tree-insert-msg msg)) - (let ((new-end (line-end-position))) - (goto-char (if (= init-point end) - new-end - (min init-point (- new-end 1))))))) - -(defun notmuch-tree-tag-update-display (&optional tag-changes) - "Update display for TAG-CHANGES to current message. - -Updates the message in the message pane if appropriate, but does -NOT change the database." - (let* ((current-tags (notmuch-tree-get-tags)) - (new-tags (notmuch-update-tags current-tags tag-changes)) - (tree-msg-id (notmuch-tree-get-message-id))) - (unless (equal current-tags new-tags) - (notmuch-tree-set-tags new-tags) - (notmuch-tree-refresh-result) - (when (window-live-p notmuch-tree-message-window) - (with-selected-window notmuch-tree-message-window - (when (string= tree-msg-id (notmuch-show-get-message-id)) - (notmuch-show-update-tags new-tags))))))) - -;;; Commands (and some helper functions used by them) - -(defun notmuch-tree-tag (tag-changes) - "Change tags for the current message." - (interactive - (list (notmuch-read-tag-changes (notmuch-tree-get-tags) "Tag message"))) - (notmuch-tag (notmuch-tree-get-message-id) tag-changes) - (notmuch-tree-tag-update-display tag-changes)) - -(defun notmuch-tree-add-tag (tag-changes) - "Same as `notmuch-tree-tag' but sets initial input to '+'." - (interactive - (list (notmuch-read-tag-changes (notmuch-tree-get-tags) "Tag message" "+"))) - (notmuch-tree-tag tag-changes)) - -(defun notmuch-tree-remove-tag (tag-changes) - "Same as `notmuch-tree-tag' but sets initial input to '-'." - (interactive - (list (notmuch-read-tag-changes (notmuch-tree-get-tags) "Tag message" "-"))) - (notmuch-tree-tag tag-changes)) - -(defun notmuch-tree-resume-message () - "Resume EDITING the current draft message." - (interactive) - (notmuch-tree-close-message-window) - (let ((id (notmuch-tree-get-message-id))) - (if id - (notmuch-draft-resume id) - (message "No message to resume!")))) - -;; The next two functions close the message window before calling -;; notmuch-search or notmuch-tree but they do so after the user has -;; entered the query (in case the user was basing the query on -;; something in the message window). - -(defun notmuch-tree-to-search () - "Run \"notmuch search\" with the given `query' and display results." - (interactive) - (let ((query (notmuch-read-query "Notmuch search: "))) - (notmuch-tree-close-message-window) - (notmuch-search query))) - -(defun notmuch-tree-to-tree () - "Run a query and display results in tree view." - (interactive) - (let ((query (notmuch-read-query "Notmuch tree view search: "))) - (notmuch-tree-close-message-window) - (notmuch-tree query))) - -(defun notmuch-tree-archive-thread-then-next () - "Archive all messages in the current buffer, then show next thread from search." - (interactive) - (notmuch-tree-archive-thread) - (notmuch-tree-next-thread)) - -(defun notmuch-unthreaded-from-tree-current-query () - "Switch from tree view to unthreaded view." - (interactive) - (unless notmuch-tree-unthreaded - (notmuch-tree-refresh-view 'unthreaded))) - -(defun notmuch-tree-from-unthreaded-current-query () - "Switch from unthreaded view to tree view." - (interactive) - (when notmuch-tree-unthreaded - (notmuch-tree-refresh-view 'tree))) - -(defun notmuch-search-from-tree-current-query () - "Call notmuch search with the current query." - (interactive) - (notmuch-tree-close-message-window) - (notmuch-search (notmuch-tree-get-query))) - -(defun notmuch-tree-message-window-kill-hook () - "Close the message pane when exiting the show buffer." - (let ((buffer (current-buffer))) - (when (and (window-live-p notmuch-tree-message-window) - (eq (window-buffer notmuch-tree-message-window) buffer)) - ;; We could check whether this is the only window in its frame, - ;; but simply ignoring the error that is thrown otherwise is - ;; what we had to do for Emacs 24 and we stick to that because - ;; it is still the simplest approach. - (ignore-errors - (delete-window notmuch-tree-message-window))))) - -(defun notmuch-tree-command-hook () - (when (eq major-mode 'notmuch-tree-mode) - ;; We just run the notmuch-show-command-hook on the message pane. - (when (buffer-live-p notmuch-tree-message-buffer) - (with-current-buffer notmuch-tree-message-buffer - (notmuch-show-command-hook))))) - -(defun notmuch-tree-show-message-in () - "Show the current message (in split-pane)." - (interactive) - (let ((id (notmuch-tree-get-message-id)) - (inhibit-read-only t) - buffer) - (when id - ;; We close and reopen the window to kill off un-needed buffers - ;; this might cause flickering but seems ok. - (notmuch-tree-close-message-window) - (setq notmuch-tree-message-window - (split-window-vertically (/ (window-height) 4))) - (with-selected-window notmuch-tree-message-window - (let (;; Since we are only displaying one message do not indent. - (notmuch-show-indent-messages-width 0) - (notmuch-show-single-message t) - ;; Ensure that `pop-to-buffer-same-window' uses the - ;; window we want it to use. - (display-buffer-overriding-action - '((display-buffer-same-window) - (inhibit-same-window . nil)))) - (setq buffer (notmuch-show id)))) - ;; We need the `let' as notmuch-tree-message-window is buffer local. - (let ((window notmuch-tree-message-window)) - (with-current-buffer buffer - (setq notmuch-tree-message-window window) - (add-hook 'kill-buffer-hook 'notmuch-tree-message-window-kill-hook))) - (when notmuch-show-mark-read-tags - (notmuch-tree-tag-update-display notmuch-show-mark-read-tags)) - (setq notmuch-tree-message-buffer buffer)))) - -(defun notmuch-tree-show-message-out () - "Show the current message (in whole window)." - (interactive) - (let ((id (notmuch-tree-get-message-id)) - (inhibit-read-only t)) - (when id - ;; We close the window to kill off un-needed buffers. - (notmuch-tree-close-message-window) - ;; n-s-s-m is buffer local, so use inner let. - (let ((notmuch-show-single-message t)) - (notmuch-show id))))) - -(defun notmuch-tree-show-message (arg) - "Show the current message. - -Shows in split pane or whole window according to value of -`notmuch-tree-show-out'. A prefix argument reverses the choice." - (interactive "P") - (if (or (and (notmuch-tree-show-out) (not arg)) - (and (not (notmuch-tree-show-out)) arg)) - (notmuch-tree-show-message-out) - (notmuch-tree-show-message-in))) - -(defun notmuch-tree-scroll-message-window () - "Scroll the message window (if it exists)." - (interactive) - (when (window-live-p notmuch-tree-message-window) - (with-selected-window notmuch-tree-message-window - (if (pos-visible-in-window-p (point-max)) - t - (scroll-up))))) - -(defun notmuch-tree-scroll-message-window-back () - "Scroll the message window back (if it exists)." - (interactive) - (when (window-live-p notmuch-tree-message-window) - (with-selected-window notmuch-tree-message-window - (if (pos-visible-in-window-p (point-min)) - t - (scroll-down))))) - -(defun notmuch-tree-scroll-or-next () - "Scroll the message window. -If it at end go to next message." - (interactive) - (when (notmuch-tree-scroll-message-window) - (notmuch-tree-next-matching-message))) - -(defun notmuch-tree-quit (&optional kill-both) - "Close the split view or exit tree." - (interactive "P") - (when (or (not (notmuch-tree-close-message-window)) kill-both) - (kill-buffer (current-buffer)))) - -(defun notmuch-tree-close-message-window () - "Close the message-window. Return t if close succeeds." - (interactive) - (when (and (window-live-p notmuch-tree-message-window) - (eq (window-buffer notmuch-tree-message-window) - notmuch-tree-message-buffer)) - (delete-window notmuch-tree-message-window) - (unless (get-buffer-window-list notmuch-tree-message-buffer) - (kill-buffer notmuch-tree-message-buffer)) - t)) - -(defun notmuch-tree-archive-message (&optional unarchive) - "Archive the current message. - -Archive the current message by applying the tag changes in -`notmuch-archive-tags' to it. If a prefix argument is given, the -message will be \"unarchived\", i.e. the tag changes in -`notmuch-archive-tags' will be reversed." - (interactive "P") - (when notmuch-archive-tags - (notmuch-tree-tag - (notmuch-tag-change-list notmuch-archive-tags unarchive)))) - -(defun notmuch-tree-archive-message-then-next (&optional unarchive) - "Archive the current message and move to next matching message." - (interactive "P") - (notmuch-tree-archive-message unarchive) - (notmuch-tree-next-matching-message)) - -(defun notmuch-tree-archive-thread-then-exit () - "Archive all messages in the current buffer, then exit notmuch-tree." - (interactive) - (notmuch-tree-archive-thread) - (notmuch-tree-quit t)) - -(defun notmuch-tree-archive-message-then-next-or-exit () - "Archive current message, then show next open message in current thread. - -If at the last open message in the current thread, then exit back -to search results." - (interactive) - (notmuch-tree-archive-message) - (notmuch-tree-next-matching-message t)) - -(defun notmuch-tree-next-message () - "Move to next message." - (interactive) - (forward-line) - (when (window-live-p notmuch-tree-message-window) - (notmuch-tree-show-message-in))) - -(defun notmuch-tree-prev-message () - "Move to previous message." - (interactive) - (forward-line -1) - (when (window-live-p notmuch-tree-message-window) - (notmuch-tree-show-message-in))) - -(defun notmuch-tree-goto-matching-message (&optional prev) - "Move to the next or previous matching message. - -Returns t if there was a next matching message in the thread to show, -nil otherwise." - (let ((dir (if prev -1 nil)) - (eobfn (if prev #'bobp #'eobp))) - (while (and (not (funcall eobfn)) - (not (notmuch-tree-get-match))) - (forward-line dir)) - (not (funcall eobfn)))) - -(defun notmuch-tree-matching-message (&optional prev pop-at-end) - "Move to the next or previous matching message." - (interactive "P") - (forward-line (if prev -1 nil)) - (if (and (not (notmuch-tree-goto-matching-message prev)) pop-at-end) - (notmuch-tree-quit pop-at-end) - (when (window-live-p notmuch-tree-message-window) - (notmuch-tree-show-message-in)))) - -(defun notmuch-tree-prev-matching-message (&optional pop-at-end) - "Move to previous matching message." - (interactive "P") - (notmuch-tree-matching-message t pop-at-end)) - -(defun notmuch-tree-next-matching-message (&optional pop-at-end) - "Move to next matching message." - (interactive "P") - (notmuch-tree-matching-message nil pop-at-end)) - -(defun notmuch-tree-refresh-view (&optional view) - "Refresh view." - (interactive) - (when (get-buffer-process (current-buffer)) - (error "notmuch tree process already running for current buffer")) - (let ((inhibit-read-only t) - (basic-query notmuch-tree-basic-query) - (unthreaded (cond ((eq view 'unthreaded) t) - ((eq view 'tree) nil) - (t notmuch-tree-unthreaded))) - (query-context notmuch-tree-query-context) - (target (notmuch-tree-get-message-id))) - (erase-buffer) - (notmuch-tree-worker basic-query - query-context - target - nil - unthreaded - notmuch-search-oldest-first))) - -(defun notmuch-tree-thread-top () - (when (notmuch-tree-get-message-properties) - (while (not (or (notmuch-tree-get-prop :first) (eobp))) - (forward-line -1)))) - -(defun notmuch-tree-prev-thread-in-tree () - "Move to the previous thread in the current tree" - (interactive) - (forward-line -1) - (notmuch-tree-thread-top) - (not (bobp))) - -(defun notmuch-tree-next-thread-in-tree () - "Get the next thread in the current tree. Returns t if a thread was -found or nil if not." - (interactive) - (forward-line 1) - (while (not (or (notmuch-tree-get-prop :first) (eobp))) - (forward-line 1)) - (not (eobp))) - -(defun notmuch-tree-next-thread-from-search (&optional previous) - "Move to the next thread in the parent search results, if any. - -If PREVIOUS is non-nil, move to the previous item in the -search results instead." - (interactive "P") - (let ((parent-buffer notmuch-tree-parent-buffer)) - (notmuch-tree-quit t) - (when (buffer-live-p parent-buffer) - (switch-to-buffer parent-buffer) - (if previous - (notmuch-search-previous-thread) - (notmuch-search-next-thread)) - (notmuch-tree-from-search-thread)))) - -(defun notmuch-tree-next-thread (&optional previous) - "Move to the next thread in the current tree or parent search results. - -If PREVIOUS is non-nil, move to the previous thread in the tree or -search results instead." - (interactive) - (unless (if previous (notmuch-tree-prev-thread-in-tree) - (notmuch-tree-next-thread-in-tree)) - (notmuch-tree-next-thread-from-search previous))) - -(defun notmuch-tree-prev-thread () - "Move to the previous thread in the current tree or parent search results." - (interactive) - (notmuch-tree-next-thread t)) - -(defun notmuch-tree-thread-mapcar (function) - "Call FUNCTION for each message in the current thread. -FUNCTION is called for side effects only." - (save-excursion - (notmuch-tree-thread-top) - (cl-loop collect (funcall function) - do (forward-line) - while (and (notmuch-tree-get-message-properties) - (not (notmuch-tree-get-prop :first)))))) - -(defun notmuch-tree-get-messages-ids-thread-search () - "Return a search string for all message ids of messages in the current thread." - (mapconcat 'identity - (notmuch-tree-thread-mapcar 'notmuch-tree-get-message-id) - " or ")) - -(defun notmuch-tree-tag-thread (tag-changes) - "Tag all messages in the current thread." - (interactive - (let ((tags (apply #'append (notmuch-tree-thread-mapcar - (lambda () (notmuch-tree-get-tags)))))) - (list (notmuch-read-tag-changes tags "Tag thread")))) - (when (notmuch-tree-get-message-properties) - (notmuch-tag (notmuch-tree-get-messages-ids-thread-search) tag-changes) - (notmuch-tree-thread-mapcar - (lambda () (notmuch-tree-tag-update-display tag-changes))))) - -(defun notmuch-tree-archive-thread (&optional unarchive) - "Archive each message in thread. - -Archive each message currently shown by applying the tag changes -in `notmuch-archive-tags' to each. If a prefix argument is given, -the messages will be \"unarchived\", i.e. the tag changes in -`notmuch-archive-tags' will be reversed. - -Note: This command is safe from any race condition of new messages -being delivered to the same thread. It does not archive the -entire thread, but only the messages shown in the current -buffer." - (interactive "P") - (when notmuch-archive-tags - (notmuch-tree-tag-thread - (notmuch-tag-change-list notmuch-archive-tags unarchive)))) - -;;; Functions for displaying the tree buffer itself - -(defun notmuch-tree-clean-address (address) - "Try to clean a single email ADDRESS for display. Return -AUTHOR_NAME if present, otherwise return AUTHOR_EMAIL. Return -unchanged ADDRESS if parsing fails." - (let* ((clean-address (notmuch-clean-address address)) - (p-address (car clean-address)) - (p-name (cdr clean-address))) - - ;; If we have a name return that otherwise return the address. - (or p-name p-address))) - -(defun notmuch-tree-format-field (field format-string msg) - "Format a FIELD of MSG according to FORMAT-STRING and return string." - (let* ((headers (plist-get msg :headers)) - (match (plist-get msg :match))) - (cond - ((listp field) - (format format-string (notmuch-tree-format-field-list field msg))) - - ((functionp field) - (funcall field format-string msg)) - - ((string-equal field "date") - (let ((face (if match - 'notmuch-tree-match-date-face - 'notmuch-tree-no-match-date-face))) - (propertize (format format-string (plist-get msg :date_relative)) - 'face face))) - - ((string-equal field "tree") - (let ((tree-status (plist-get msg :tree-status)) - (face (if match - 'notmuch-tree-match-tree-face - 'notmuch-tree-no-match-tree-face))) - - (propertize (format format-string - (mapconcat #'identity (reverse tree-status) "")) - 'face face))) - - ((string-equal field "subject") - (let ((bare-subject (notmuch-show-strip-re (plist-get headers :Subject))) - (previous-subject notmuch-tree-previous-subject) - (face (if match - 'notmuch-tree-match-subject-face - 'notmuch-tree-no-match-subject-face))) - - (setq notmuch-tree-previous-subject bare-subject) - (propertize (format format-string - (if (string= previous-subject bare-subject) - " ..." - bare-subject)) - 'face face))) - - ((string-equal field "authors") - (let ((author (notmuch-tree-clean-address (plist-get headers :From))) - (len (length (format format-string ""))) - (face (if match - 'notmuch-tree-match-author-face - 'notmuch-tree-no-match-author-face))) - (when (> (length author) len) - (setq author (substring author 0 len))) - (propertize (format format-string author) 'face face))) - - ((string-equal field "tags") - (let ((tags (plist-get msg :tags)) - (orig-tags (plist-get msg :orig-tags)) - (face (if match - 'notmuch-tree-match-tag-face - 'notmuch-tree-no-match-tag-face))) - (format format-string (notmuch-tag-format-tags tags orig-tags face))))))) - -(defun notmuch-tree-format-field-list (field-list msg) - "Format fields of MSG according to FIELD-LIST and return string." - (let ((face (if (plist-get msg :match) - 'notmuch-tree-match-face - 'notmuch-tree-no-match-face)) - (result-string)) - (dolist (spec field-list result-string) - (let ((field-string (notmuch-tree-format-field (car spec) (cdr spec) msg))) - (setq result-string (concat result-string field-string)))) - (notmuch-apply-face result-string face t))) - -(defun notmuch-tree-insert-msg (msg) - "Insert the message MSG according to notmuch-tree-result-format." - ;; We need to save the previous subject as it will get overwritten - ;; by the insert-field calls. - (let ((previous-subject notmuch-tree-previous-subject)) - (insert (notmuch-tree-format-field-list (notmuch-tree-result-format) msg)) - (notmuch-tree-set-message-properties msg) - (notmuch-tree-set-prop :previous-subject previous-subject) - (insert "\n"))) - -(defun notmuch-tree-goto-and-insert-msg (msg) - "Insert msg at the end of the buffer. Move point to msg if it is the target." - (save-excursion - (goto-char (point-max)) - (notmuch-tree-insert-msg msg)) - (let ((msg-id (notmuch-id-to-query (plist-get msg :id))) - (target notmuch-tree-target-msg)) - (when (or (and (not target) (plist-get msg :match)) - (string= msg-id target)) - (setq notmuch-tree-target-msg "found") - (goto-char (point-max)) - (forward-line -1) - (when notmuch-tree-open-target - (notmuch-tree-show-message-in) - (notmuch-tree-command-hook))))) - -(defun notmuch-tree-insert-tree (tree depth tree-status first last) - "Insert the message tree TREE at depth DEPTH in the current thread. - -A message tree is another name for a single sub-thread: i.e., a -message together with all its descendents." - (let ((msg (car tree)) - (replies (cadr tree)) - ;; outline level, computed from the message's depth and - ;; whether or not it's the first message in the tree. - (level (1+ (if (and (eq 0 depth) (not first)) 1 depth)))) - (cond - ((and (< 0 depth) (not last)) - (push (alist-get 'vertical-tee notmuch-tree-thread-symbols) tree-status)) - ((and (< 0 depth) last) - (push (alist-get 'bottom notmuch-tree-thread-symbols) tree-status)) - ((and (eq 0 depth) first last) - (push (alist-get 'prefix notmuch-tree-thread-symbols) tree-status)) - ((and (eq 0 depth) first (not last)) - (push (alist-get 'top-tee notmuch-tree-thread-symbols) tree-status)) - ((and (eq 0 depth) (not first) last) - (push (alist-get 'bottom notmuch-tree-thread-symbols) tree-status)) - ((and (eq 0 depth) (not first) (not last)) - (push (alist-get 'vertical-tee notmuch-tree-thread-symbols) tree-status))) - (push (concat (alist-get (if replies 'top-tee 'top) notmuch-tree-thread-symbols) - (alist-get 'arrow notmuch-tree-thread-symbols)) - tree-status) - (setq msg (plist-put msg :first (and first (eq 0 depth)))) - (setq msg (plist-put msg :tree-status tree-status)) - (setq msg (plist-put msg :orig-tags (plist-get msg :tags))) - (setq msg (plist-put msg :level level)) - (notmuch-tree-goto-and-insert-msg msg) - (pop tree-status) - (pop tree-status) - (if last - (push " " tree-status) - (push (alist-get 'vertical notmuch-tree-thread-symbols) tree-status)) - (notmuch-tree-insert-thread replies (1+ depth) tree-status))) - -(defun notmuch-tree-insert-thread (thread depth tree-status) - "Insert the collection of sibling sub-threads THREAD at depth -DEPTH in the current forest." - (let ((n (length thread))) - (cl-loop for tree in thread - for count from 1 to n - do (notmuch-tree-insert-tree tree depth tree-status - (eq count 1) - (eq count n))))) - -(defun notmuch-tree-insert-forest-thread (forest-thread) - "Insert a single complete thread." - ;; Reset at the start of each main thread. - (setq notmuch-tree-previous-subject nil) - (notmuch-tree-insert-thread forest-thread 0 nil)) - -(defun notmuch-tree-insert-forest (forest) - "Insert a forest of threads. - -This function inserts a collection of several complete threads as -passed to it by notmuch-tree-process-filter." - (mapc 'notmuch-tree-insert-forest-thread forest)) - -(define-derived-mode notmuch-tree-mode fundamental-mode "notmuch-tree" - "Major mode displaying messages (as opposed to threads) of a notmuch search. - -This buffer contains the results of a \"notmuch tree\" of your -email archives. Each line in the buffer represents a single -message giving the relative date, the author, subject, and any -tags. - -Pressing \\[notmuch-tree-show-message] on any line displays that message. - -Complete list of currently available key bindings: - -\\{notmuch-tree-mode-map}" - (setq notmuch-buffer-refresh-function #'notmuch-tree-refresh-view) - (hl-line-mode 1) - (setq buffer-read-only t) - (setq truncate-lines t) - (when notmuch-tree-outline-enabled (notmuch-tree-outline-mode 1))) - -(defvar notmuch-tree-process-exit-functions nil - "Functions called when the process inserting a tree of results finishes. - -Functions in this list are called with one argument, the process -object, and with the tree results buffer as the current buffer.") - -(defun notmuch-tree-process-sentinel (proc _msg) - "Add a message to let user know when \"notmuch tree\" exits." - (let ((buffer (process-buffer proc)) - (status (process-status proc)) - (exit-status (process-exit-status proc))) - (when (memq status '(exit signal)) - (kill-buffer (process-get proc 'parse-buf)) - (when (buffer-live-p buffer) - (with-current-buffer buffer - (save-excursion - (let ((inhibit-read-only t)) - (goto-char (point-max)) - (when (eq status 'signal) - (insert "Incomplete search results (tree view process was killed).\n")) - (when (eq status 'exit) - (insert "End of search results.") - (unless (= exit-status 0) - (insert (format " (process returned %d)" exit-status))) - (insert "\n")))) - (run-hook-with-args 'notmuch-tree-process-exit-functions proc)))))) - -(defun notmuch-tree-process-filter (proc string) - "Process and filter the output of \"notmuch show\" for tree view." - (let ((results-buf (process-buffer proc)) - (parse-buf (process-get proc 'parse-buf)) - (inhibit-read-only t)) - (if (not (buffer-live-p results-buf)) - (delete-process proc) - (with-current-buffer parse-buf - ;; Insert new data - (save-excursion - (goto-char (point-max)) - (insert string)) - (notmuch-sexp-parse-partial-list 'notmuch-tree-insert-forest-thread - results-buf))))) - -(defun notmuch-tree-worker (basic-query &optional query-context target - open-target unthreaded oldest-first) - "Insert the tree view of the search in the current buffer. - -This is is a helper function for notmuch-tree. The arguments are -the same as for the function notmuch-tree." - (interactive) - (notmuch-tree-mode) - (add-hook 'post-command-hook #'notmuch-tree-command-hook t t) - (setq notmuch-search-oldest-first oldest-first) - (setq notmuch-tree-unthreaded unthreaded) - (setq notmuch-tree-basic-query basic-query) - (setq notmuch-tree-query-context (if (or (string= query-context "") - (string= query-context "*")) - nil - query-context)) - (setq notmuch-tree-target-msg target) - (setq notmuch-tree-open-target open-target) - ;; Set the default value for `notmuch-show-process-crypto' in this - ;; buffer. Although we don't use this some of the functions we call - ;; (such as reply) do. It is a buffer local variable so setting it - ;; will not affect genuine show buffers. - (setq notmuch-show-process-crypto notmuch-crypto-process-mime) - (erase-buffer) - (goto-char (point-min)) - (let* ((search-args (concat basic-query - (and query-context - (concat " and (" query-context ")")))) - (sort-arg (if oldest-first "--sort=oldest-first" "--sort=newest-first")) - (message-arg (if unthreaded "--unthreaded" "--entire-thread"))) - (when (equal (car (notmuch--process-lines notmuch-command "count" search-args)) "0") - (setq search-args basic-query)) - (notmuch-tag-clear-cache) - (let ((proc (notmuch-start-notmuch - "notmuch-tree" (current-buffer) #'notmuch-tree-process-sentinel - "show" "--body=false" "--format=sexp" "--format-version=5" - sort-arg message-arg search-args)) - ;; Use a scratch buffer to accumulate partial output. - ;; This buffer will be killed by the sentinel, which - ;; should be called no matter how the process dies. - (parse-buf (generate-new-buffer " *notmuch tree parse*"))) - (process-put proc 'parse-buf parse-buf) - (set-process-filter proc 'notmuch-tree-process-filter) - (set-process-query-on-exit-flag proc nil)))) - -(defun notmuch-tree-get-query () - "Return the current query in this tree buffer." - (if notmuch-tree-query-context - (concat notmuch-tree-basic-query - " and (" - notmuch-tree-query-context - ")") - notmuch-tree-basic-query)) - -(defun notmuch-tree-toggle-order () - "Toggle the current search order. - -This command toggles the sort order for the current search. The -default sort order is defined by `notmuch-search-oldest-first'." - (interactive) - (setq notmuch-search-oldest-first (not notmuch-search-oldest-first)) - (notmuch-tree-refresh-view)) - -(defun notmuch-tree (&optional query query-context target buffer-name - open-target unthreaded parent-buffer oldest-first) - "Display threads matching QUERY in tree view. - -The arguments are: - QUERY: the main query. This can be any query but in many cases will be - a single thread. If nil this is read interactively from the minibuffer. - QUERY-CONTEXT: is an additional term for the query. The query used - is QUERY and QUERY-CONTEXT unless that does not match any messages - in which case we fall back to just QUERY. - TARGET: A message ID (with the id: prefix) that will be made - current if it appears in the tree view results. - BUFFER-NAME: the name of the buffer to display the tree view. If - it is nil \"*notmuch-tree\" followed by QUERY is used. - OPEN-TARGET: If TRUE open the target message in the message pane. - UNTHREADED: If TRUE only show matching messages in an unthreaded view." - (interactive) - (unless query - (setq query (notmuch-read-query (concat "Notmuch " - (if unthreaded "unthreaded " "tree ") - "view search: ")))) - (let* ((name - (or buffer-name - (notmuch-search-buffer-title query - (if unthreaded "unthreaded" "tree")))) - (buffer (get-buffer-create (generate-new-buffer-name name))) - (inhibit-read-only t)) - (pop-to-buffer-same-window buffer)) - ;; Don't track undo information for this buffer - (setq buffer-undo-list t) - (notmuch-tree-worker query query-context target open-target unthreaded oldest-first) - (setq notmuch-tree-parent-buffer parent-buffer) - (setq truncate-lines t)) - -(defun notmuch-unthreaded (&optional query query-context target buffer-name - open-target) - "Display threads matching QUERY in unthreaded view. - -See function NOTMUCH-TREE for documentation of the arguments" - (interactive) - (notmuch-tree query query-context target buffer-name open-target t)) - -(defun notmuch-tree-filter (query) - "Filter or LIMIT the current search results based on an additional query string. - -Runs a new tree search matching only messages that match both the -current search results AND the additional query string provided." - (interactive (list (notmuch-read-query "Filter search: "))) - (let ((notmuch-show-process-crypto (notmuch-tree--message-process-crypto)) - (grouped-query (notmuch-group-disjunctive-query-string query)) - (grouped-original-query (notmuch-group-disjunctive-query-string - (notmuch-tree-get-query)))) - (notmuch-tree-close-message-window) - (notmuch-tree (if (string= grouped-original-query "*") - grouped-query - (concat grouped-original-query " and " grouped-query))))) - -(defun notmuch-tree-filter-by-tag (tag) - "Filter the current search results based on a single TAG. - -Run a new search matching only messages that match the current -search results and that are also tagged with the given TAG." - (interactive - (list (notmuch-select-tag-with-completion "Filter by tag: " - notmuch-tree-basic-query))) - (let ((notmuch-show-process-crypto (notmuch-tree--message-process-crypto))) - (notmuch-tree-close-message-window) - (notmuch-tree (concat notmuch-tree-basic-query " and tag:" tag) - notmuch-tree-query-context - nil - nil - nil - notmuch-tree-unthreaded - nil - notmuch-search-oldest-first))) - -(defun notmuch-tree-edit-search (query) - "Edit the current search" - (interactive (list (read-from-minibuffer "Edit search: " - notmuch-tree-basic-query))) - (let ((notmuch-show-process-crypto (notmuch-tree--message-process-crypto))) - (notmuch-tree-close-message-window) - (notmuch-tree query - notmuch-tree-query-context - nil - nil - nil - notmuch-tree-unthreaded - nil - notmuch-search-oldest-first))) - -;;; Tree outline mode -;;;; Custom variables -(defcustom notmuch-tree-outline-enabled nil - "Whether to automatically activate `notmuch-tree-outline-mode' in tree views." - :type 'boolean) - -(defcustom notmuch-tree-outline-visibility 'hide-others - "Default state of the forest outline for `notmuch-tree-outline-mode'. - -This variable controls the state of a forest initially and after -a movement command. If set to nil, all trees are displayed while -the symbol hide-all indicates that all trees in the forest should -be folded and hide-other that only the first one should be -unfolded." - :type '(choice (const :tag "Show all" nil) - (const :tag "Hide others" hide-others) - (const :tag "Hide all" hide-all))) - -(defcustom notmuch-tree-outline-auto-close nil - "Close message and tree windows when moving past the last message." - :type 'boolean) - -(defcustom notmuch-tree-outline-open-on-next nil - "Open new messages under point if they are closed when moving to next one. - -When this flag is set, using the command -`notmuch-tree-outline-next' with point on a header for a new -message that is not shown will open its `notmuch-show' buffer -instead of moving point to next matching message." - :type 'boolean) - -;;;; Helper functions -(defsubst notmuch-tree-outline--pop-at-end (pop-at-end) - (if notmuch-tree-outline-auto-close (not pop-at-end) pop-at-end)) - -(defun notmuch-tree-outline--set-visibility () - (when (and notmuch-tree-outline-mode (> (point-max) (point-min))) - (cl-case notmuch-tree-outline-visibility - (hide-others (notmuch-tree-outline-hide-others)) - (hide-all (outline-hide-body))))) - -(defun notmuch-tree-outline--on-exit (proc) - (when (eq (process-status proc) 'exit) - (notmuch-tree-outline--set-visibility))) - -(add-hook 'notmuch-tree-process-exit-functions #'notmuch-tree-outline--on-exit) - -(defsubst notmuch-tree-outline--level (&optional props) - (or (plist-get (or props (notmuch-tree-get-message-properties)) :level) 0)) - -(defsubst notmuch-tree-outline--message-open-p () - (and (buffer-live-p notmuch-tree-message-buffer) - (get-buffer-window notmuch-tree-message-buffer) - (let ((id (notmuch-tree-get-message-id))) - (and id - (with-current-buffer notmuch-tree-message-buffer - (string= (notmuch-show-get-message-id) id)))))) - -(defsubst notmuch-tree-outline--at-original-match-p () - (and (notmuch-tree-get-prop :match) - (equal (notmuch-tree-get-prop :orig-tags) - (notmuch-tree-get-prop :tags)))) - -(defun notmuch-tree-outline--next (prev thread pop-at-end &optional open-new) - (cond (thread - (notmuch-tree-thread-top) - (if prev - (outline-backward-same-level 1) - (outline-forward-same-level 1)) - (when (> (notmuch-tree-outline--level) 0) (outline-show-branches)) - (notmuch-tree-outline--next nil nil pop-at-end t)) - ((and (or open-new notmuch-tree-outline-open-on-next) - (notmuch-tree-outline--at-original-match-p) - (not (notmuch-tree-outline--message-open-p))) - (notmuch-tree-outline-hide-others t)) - (t (outline-next-visible-heading (if prev -1 1)) - (unless (notmuch-tree-get-prop :match) - (notmuch-tree-matching-message prev pop-at-end)) - (notmuch-tree-outline-hide-others t)))) - -;;;; User commands -(defun notmuch-tree-outline-hide-others (&optional and-show) - "Fold all threads except the one around point. -If AND-SHOW is t, make the current message visible if it's not." - (interactive) - (save-excursion - (while (and (not (bobp)) (> (notmuch-tree-outline--level) 1)) - (outline-previous-heading)) - (outline-hide-sublevels 1)) - (when (> (notmuch-tree-outline--level) 0) - (outline-show-subtree) - (when and-show (notmuch-tree-show-message nil)))) - -(defun notmuch-tree-outline-next (&optional pop-at-end) - "Next matching message in a forest, taking care of thread visibility. -A prefix argument reverses the meaning of `notmuch-tree-outline-auto-close'." - (interactive "P") - (let ((pop (notmuch-tree-outline--pop-at-end pop-at-end))) - (if (null notmuch-tree-outline-visibility) - (notmuch-tree-matching-message nil pop) - (notmuch-tree-outline--next nil nil pop)))) - -(defun notmuch-tree-outline-previous (&optional pop-at-end) - "Previous matching message in forest, taking care of thread visibility. -With prefix, quit the tree view if there is no previous message." - (interactive "P") - (if (null notmuch-tree-outline-visibility) - (notmuch-tree-prev-matching-message pop-at-end) - (notmuch-tree-outline--next t nil pop-at-end))) - -(defun notmuch-tree-outline-next-thread () - "Next matching thread in forest, taking care of thread visibility." - (interactive) - (if (null notmuch-tree-outline-visibility) - (notmuch-tree-next-thread) - (notmuch-tree-outline--next nil t nil))) - -(defun notmuch-tree-outline-previous-thread () - "Previous matching thread in forest, taking care of thread visibility." - (interactive) - (if (null notmuch-tree-outline-visibility) - (notmuch-tree-prev-thread) - (notmuch-tree-outline--next t t nil))) - -;;;; Mode definition -(defvar notmuch-tree-outline-mode-lighter nil - "The lighter mark for notmuch-tree-outline mode. -Usually empty since outline-minor-mode's lighter will be active.") - -(define-minor-mode notmuch-tree-outline-mode - "Minor mode allowing message trees to be folded as outlines. - -When this mode is set, each thread and subthread in the results -list is treated as a foldable section, with its first message as -its header. - -The mode just makes available in the tree buffer all the -keybindings in `outline-minor-mode', and binds the following -additional keys: - -\\{notmuch-tree-outline-mode-map} - -The customizable variable `notmuch-tree-outline-visibility' -controls how navigation in the buffer is affected by this mode: - - - If it is set to nil, `notmuch-tree-outline-previous', - `notmuch-tree-outline-next', and their thread counterparts - behave just as the corresponding notmuch-tree navigation keys - when this mode is not enabled. - - - If, on the other hand, `notmuch-tree-outline-visibility' is - set to a non-nil value, these commands hiding the outlines of - the trees you are not reading as you move to new messages. - -To enable notmuch-tree-outline-mode by default in all -notmuch-tree buffers, just set -`notmuch-tree-outline-mode-enabled' to t." - :lighter notmuch-tree-outline-mode-lighter - :keymap `((,(kbd "TAB") . outline-cycle) - (,(kbd "M-TAB") . outline-cycle-buffer) - ("n" . notmuch-tree-outline-next) - ("p" . notmuch-tree-outline-previous) - (,(kbd "M-n") . notmuch-tree-outline-next-thread) - (,(kbd "M-p") . notmuch-tree-outline-previous-thread)) - (outline-minor-mode notmuch-tree-outline-mode) - (unless (derived-mode-p 'notmuch-tree-mode) - (user-error "notmuch-tree-outline-mode is only meaningful for notmuch trees!")) - (if notmuch-tree-outline-mode - (progn (setq-local outline-regexp "^[^\n]+") - (setq-local outline-level #'notmuch-tree-outline--level) - (notmuch-tree-outline--set-visibility)) - (setq-local outline-regexp (default-value 'outline-regexp)) - (setq-local outline-level (default-value 'outline-level)))) - -;;; _ - -(provide 'notmuch-tree) - -;;; notmuch-tree.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-tree.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-tree.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-wash.el b/emacs/elpa/notmuch-20231006.2337/notmuch-wash.el @@ -1,418 +0,0 @@ -;;; notmuch-wash.el --- cleaning up message bodies -*- lexical-binding: t -*- -;; -;; Copyright © Carl Worth -;; Copyright © David Edmondson -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: Carl Worth <cworth@cworth.org> -;; David Edmondson <dme@dme.org> - -;;; Code: - -(require 'coolj) -(require 'diff-mode) -(require 'notmuch-lib) - -(declare-function notmuch-show-insert-bodypart "notmuch-show" - (msg part depth &optional hide)) -(defvar notmuch-show-indent-messages-width) - -;;; Options - -(defgroup notmuch-wash nil - "Cleaning up messages for display." - :group 'notmuch) - -(defcustom notmuch-wash-signature-regexp "^\\(-- ?\\|_+\\)$" - "Pattern to match a line that separates content from signature." - :type 'regexp - :group 'notmuch-wash) - -(defcustom notmuch-wash-citation-regexp "\\(^[[:space:]]*>.*\n\\)+" - "Pattern to match citation lines." - :type 'regexp - :group 'notmuch-wash) - -(defcustom notmuch-wash-original-regexp "^\\(--+\s?[oO]riginal [mM]essage\s?--+\\)$" - "Pattern to match a line that separates original message from -reply in top-posted message." - :type 'regexp - :group 'notmuch-wash) - -(defcustom notmuch-wash-button-signature-hidden-format - "[ %d-line signature. Click/Enter to show. ]" - "String used to construct button text for hidden signatures. -Can use up to one integer format parameter, i.e. %d." - :type 'string - :group 'notmuch-wash) - -(defcustom notmuch-wash-button-signature-visible-format - "[ %d-line signature. Click/Enter to hide. ]" - "String used to construct button text for visible signatures. -Can use up to one integer format parameter, i.e. %d." - :type 'string - :group 'notmuch-wash) - -(defcustom notmuch-wash-button-citation-hidden-format - "[ %d more citation lines. Click/Enter to show. ]" - "String used to construct button text for hidden citations. -Can use up to one integer format parameter, i.e. %d." - :type 'string - :group 'notmuch-wash) - -(defcustom notmuch-wash-button-citation-visible-format - "[ %d more citation lines. Click/Enter to hide. ]" - "String used to construct button text for visible citations. -Can use up to one integer format parameter, i.e. %d." - :type 'string - :group 'notmuch-wash) - -(defcustom notmuch-wash-button-original-hidden-format - "[ %d-line hidden original message. Click/Enter to show. ]" - "String used to construct button text for hidden citations. -Can use up to one integer format parameter, i.e. %d." - :type 'string - :group 'notmuch-wash) - -(defcustom notmuch-wash-button-original-visible-format - "[ %d-line original message. Click/Enter to hide. ]" - "String used to construct button text for visible citations. -Can use up to one integer format parameter, i.e. %d." - :type 'string - :group 'notmuch-wash) - -(defcustom notmuch-wash-signature-lines-max 12 - "Maximum length of signature that will be hidden by default." - :type 'integer - :group 'notmuch-wash) - -(defcustom notmuch-wash-citation-lines-prefix 3 - "Always show at least this many lines from the start of a citation. - -If there is one more line than the sum of -`notmuch-wash-citation-lines-prefix' and -`notmuch-wash-citation-lines-suffix', show that, otherwise -collapse the remaining lines into a button." - :type 'integer - :group 'notmuch-wash) - -(defcustom notmuch-wash-citation-lines-suffix 3 - "Always show at least this many lines from the end of a citation. - -If there is one more line than the sum of -`notmuch-wash-citation-lines-prefix' and -`notmuch-wash-citation-lines-suffix', show that, otherwise -collapse the remaining lines into a button." - :type 'integer - :group 'notmuch-wash) - -(defcustom notmuch-wash-wrap-lines-length nil - "Wrap line after at most this many characters. - -If this is nil, lines in messages will be wrapped to fit in the -current window. If this is a number, lines will be wrapped after -this many characters (ignoring indentation due to thread depth) -or at the window width (whichever one is lower)." - :type '(choice (const :tag "window width" nil) - (integer :tag "number of characters")) - :group 'notmuch-wash) - -;;; Faces - -(defface notmuch-wash-toggle-button - '((t (:inherit font-lock-comment-face))) - "Face used for buttons toggling the visibility of washed away -message parts." - :group 'notmuch-wash - :group 'notmuch-faces) - -(defface notmuch-wash-cited-text - '((t (:inherit message-cited-text))) - "Face used for cited text." - :group 'notmuch-wash - :group 'notmuch-faces) - -;;; Buttons - -(defun notmuch-wash-toggle-invisible-action (cite-button) - ;; Toggle overlay visibility - (let ((overlay (button-get cite-button 'overlay))) - (overlay-put overlay 'invisible (not (overlay-get overlay 'invisible)))) - ;; Update button text - (let* ((new-start (button-start cite-button)) - (overlay (button-get cite-button 'overlay)) - (button-label (notmuch-wash-button-label overlay)) - (old-point (point)) - (properties (text-properties-at (point))) - (inhibit-read-only t)) - (goto-char new-start) - (insert button-label) - (set-text-properties new-start (point) properties) - (let ((old-end (button-end cite-button))) - (move-overlay cite-button new-start (point)) - (delete-region (point) old-end)) - (goto-char (min old-point (1- (button-end cite-button)))))) - -(define-button-type 'notmuch-wash-button-invisibility-toggle-type - 'action 'notmuch-wash-toggle-invisible-action - 'follow-link t - 'face 'notmuch-wash-toggle-button - :supertype 'notmuch-button-type) - -(define-button-type 'notmuch-wash-button-citation-toggle-type - 'help-echo "mouse-1, RET: Show citation" - :supertype 'notmuch-wash-button-invisibility-toggle-type) - -(define-button-type 'notmuch-wash-button-signature-toggle-type - 'help-echo "mouse-1, RET: Show signature" - :supertype 'notmuch-wash-button-invisibility-toggle-type) - -(define-button-type 'notmuch-wash-button-original-toggle-type - 'help-echo "mouse-1, RET: Show original message" - :supertype 'notmuch-wash-button-invisibility-toggle-type) - -(defun notmuch-wash-region-isearch-show (overlay) - (notmuch-wash-toggle-invisible-action - (overlay-get overlay 'notmuch-wash-button))) - -(defun notmuch-wash-button-label (overlay) - (let* ((type (overlay-get overlay 'type)) - (invis-spec (overlay-get overlay 'invisible)) - (state (if (invisible-p invis-spec) "hidden" "visible")) - (label-format (symbol-value - (intern-soft - (format "notmuch-wash-button-%s-%s-format" - type state)))) - (lines-count (count-lines (overlay-start overlay) - (overlay-end overlay)))) - (format label-format lines-count))) - -(defun notmuch-wash-region-to-button (beg end type &optional prefix) - "Auxiliary function to do the actual making of overlays and buttons. - -BEG and END are buffer locations. TYPE should a string, either -\"citation\" or \"signature\". Optional PREFIX is some arbitrary -text to insert before the button, probably for indentation. Note -that PREFIX should not include a newline." - ;; This uses some slightly tricky conversions between strings and - ;; symbols because of the way the button code works. Note that - ;; replacing intern-soft with make-symbol will cause this to fail, - ;; since the newly created symbol has no plist. - (let ((overlay (make-overlay beg end)) - (button-type (intern-soft (concat "notmuch-wash-button-" - type "-toggle-type")))) - (overlay-put overlay 'invisible t) - (overlay-put overlay 'isearch-open-invisible #'notmuch-wash-region-isearch-show) - (overlay-put overlay 'type type) - (goto-char (1+ end)) - (save-excursion - (goto-char beg) - (when prefix - (insert-before-markers prefix)) - (let ((button-beg (point))) - (insert-before-markers (notmuch-wash-button-label overlay) "\n") - (let ((button (make-button button-beg (1- (point)) - 'overlay overlay - :type button-type))) - (overlay-put overlay 'notmuch-wash-button button)))))) - -;;; Hook functions - -(defun notmuch-wash-excerpt-citations (_msg _depth) - "Excerpt citations and up to one signature." - (goto-char (point-min)) - (beginning-of-line) - (when (and (< (point) (point-max)) - (re-search-forward notmuch-wash-original-regexp nil t)) - (notmuch-wash-region-to-button (match-beginning 0) - (point-max) - "original")) - (while (and (< (point) (point-max)) - (re-search-forward notmuch-wash-citation-regexp nil t)) - (let* ((cite-start (match-beginning 0)) - (cite-end (match-end 0)) - (cite-lines (count-lines cite-start cite-end))) - (overlay-put (make-overlay cite-start cite-end) - 'face 'notmuch-wash-cited-text) - (when (> cite-lines (+ notmuch-wash-citation-lines-prefix - notmuch-wash-citation-lines-suffix - 1)) - (goto-char cite-start) - (forward-line notmuch-wash-citation-lines-prefix) - (let ((hidden-start (point-marker))) - (goto-char cite-end) - (forward-line (- notmuch-wash-citation-lines-suffix)) - (notmuch-wash-region-to-button - hidden-start (point-marker) - "citation"))))) - (when (and (not (eobp)) - (re-search-forward notmuch-wash-signature-regexp nil t)) - (let ((sig-start (match-beginning 0))) - (when (<= (count-lines sig-start (point-max)) - notmuch-wash-signature-lines-max) - (let ((sig-start-marker (make-marker)) - (sig-end-marker (make-marker))) - (set-marker sig-start-marker sig-start) - (set-marker sig-end-marker (point-max)) - (overlay-put (make-overlay sig-start-marker sig-end-marker) - 'face 'message-cited-text) - (notmuch-wash-region-to-button - sig-start-marker sig-end-marker - "signature")))))) - -(defun notmuch-wash-elide-blank-lines (_msg _depth) - "Elide leading, trailing and successive blank lines." - ;; Algorithm derived from `article-strip-multiple-blank-lines' in - ;; `gnus-art.el'. - ;; Make all blank lines empty. - (goto-char (point-min)) - (while (re-search-forward "^[[:space:]\t]+$" nil t) - (replace-match "" nil t)) - ;; Replace multiple empty lines with a single empty line. - (goto-char (point-min)) - (while (re-search-forward "^\n\\(\n+\\)" nil t) - (delete-region (match-beginning 1) (match-end 1))) - ;; Remove a leading blank line. - (goto-char (point-min)) - (when (looking-at "\n") - (delete-region (match-beginning 0) (match-end 0))) - ;; Remove a trailing blank line. - (goto-char (point-max)) - (when (looking-at "\n") - (delete-region (match-beginning 0) (match-end 0)))) - -(defun notmuch-wash-tidy-citations (_msg _depth) - "Improve the display of cited regions of a message. - -Perform several transformations on the message body: - -- Remove lines of repeated citation leaders with no other - content, -- Remove citation leaders standing alone before a block of cited - text, -- Remove citation trailers standing alone after a block of cited - text." - ;; Remove lines of repeated citation leaders with no other content. - (goto-char (point-min)) - (while (re-search-forward "\\(^>[> ]*\n\\)\\{2,\\}" nil t) - (replace-match "\\1")) - ;; Remove citation leaders standing alone before a block of cited text. - (goto-char (point-min)) - (while (re-search-forward "\\(\n\\|^[^>].*\\)\n\\(^>[> ]*\n\\)" nil t) - (replace-match "\\1\n")) - ;; Remove citation trailers standing alone after a block of cited text. - (goto-char (point-min)) - (while (re-search-forward "\\(^>[> ]*\n\\)\\(^$\\|^[^>].*\\)" nil t) - (replace-match "\\2"))) - -(defun notmuch-wash-wrap-long-lines (_msg depth) - "Wrap long lines in the message. - -If `notmuch-wash-wrap-lines-length' is a number, this will wrap -the message lines to the minimum of the width of the window or -its value. Otherwise, this function will wrap long lines in the -message at the window width. When doing so, citation leaders in -the wrapped text are maintained." - (let* ((coolj-wrap-follows-window-size nil) - (indent (* depth notmuch-show-indent-messages-width)) - (limit (if (numberp notmuch-wash-wrap-lines-length) - (min (+ notmuch-wash-wrap-lines-length indent) - (window-width)) - (window-width))) - (fill-column (- limit - indent - ;; 2 to avoid poor interaction with - ;; `word-wrap'. - 2))) - (coolj-wrap-region (point-min) (point-max)))) - -;;;; Convert Inline Patches - -(defun notmuch-wash-subject-to-filename (subject &optional maxlen) - "Convert a mail SUBJECT into a filename. - -The resulting filename is similar to the names generated by \"git -format-patch\", without the leading patch sequence number -\"0001-\" and \".patch\" extension. Any leading \"[PREFIX]\" -style strings are removed prior to conversion. - -Optional argument MAXLEN is the maximum length of the resulting -filename, before trimming any trailing . and - characters." - (let* ((s (replace-regexp-in-string "^ *\\(\\[[^]]*\\] *\\)*" "" subject)) - (s (replace-regexp-in-string "[^A-Za-z0-9._]+" "-" s)) - (s (replace-regexp-in-string "\\.+" "." s)) - (s (if maxlen (substring s 0 (min (length s) maxlen)) s)) - (s (replace-regexp-in-string "[.-]*$" "" s))) - s)) - -(defun notmuch-wash-subject-to-patch-sequence-number (subject) - "Convert a patch mail SUBJECT into a patch sequence number. - -Return the patch sequence number N from the last \"[PATCH N/M]\" -style prefix in SUBJECT, or nil if such a prefix can't be found." - (and (string-match - "^ *\\(\\[[^]]*\\] *\\)*\\[[^]]*?\\([0-9]+\\)/[0-9]+[^]]*\\].*" - subject) - (string-to-number (substring subject (match-beginning 2) (match-end 2))))) - -(defun notmuch-wash-subject-to-patch-filename (subject) - "Convert a patch mail SUBJECT into a filename. - -The resulting filename is similar to the names generated by \"git -format-patch\". If the patch mail was generated and sent using -\"git format-patch/send-email\", this should re-create the -original filename the sender had." - (format "%04d-%s.patch" - (or (notmuch-wash-subject-to-patch-sequence-number subject) 1) - (notmuch-wash-subject-to-filename subject 52))) - -(defun notmuch-wash-convert-inline-patch-to-part (msg depth) - "Convert an inline patch into a fake `text/x-diff' attachment. - -Given that this function guesses whether a buffer includes a -patch and then guesses the extent of the patch, there is scope -for error." - (goto-char (point-min)) - (when (re-search-forward diff-file-header-re nil t) - (beginning-of-line -1) - (let ((patch-start (point)) - (patch-end (point-max)) - part) - (goto-char patch-start) - (when (or - ;; Patch ends with signature. - (re-search-forward notmuch-wash-signature-regexp nil t) - ;; Patch ends with bugtraq comment. - (re-search-forward "^\\*\\*\\* " nil t)) - (setq patch-end (match-beginning 0))) - (save-restriction - (narrow-to-region patch-start patch-end) - (setq part (plist-put part :content-type "inline patch")) - (setq part (plist-put part :content (buffer-string))) - (setq part (plist-put part :id -1)) - (setq part (plist-put part :filename - (notmuch-wash-subject-to-patch-filename - (plist-get - (plist-get msg :headers) :Subject)))) - (delete-region (point-min) (point-max)) - (notmuch-show-insert-bodypart nil part depth))))) - -;;; _ - -(provide 'notmuch-wash) - -;;; notmuch-wash.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch-wash.elc b/emacs/elpa/notmuch-20231006.2337/notmuch-wash.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch.el b/emacs/elpa/notmuch-20231006.2337/notmuch.el @@ -1,1239 +0,0 @@ -;;; notmuch.el --- run notmuch within emacs -*- lexical-binding: t -*- -;; -;; Copyright © Carl Worth -;; -;; This file is part of Notmuch. -;; -;; Notmuch 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. -;; -;; Notmuch 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 Notmuch. If not, see <https://www.gnu.org/licenses/>. -;; -;; Authors: Carl Worth <cworth@cworth.org> -;; Homepage: https://notmuchmail.org - -;;; Commentary: - -;; This is an emacs-based interface to the notmuch mail system. -;; -;; You will first need to have the notmuch program installed and have a -;; notmuch database built in order to use this. See -;; https://notmuchmail.org for details. -;; -;; To install this software, copy it to a directory that is on the -;; `load-path' variable within emacs (a good candidate is -;; /usr/local/share/emacs/site-lisp). If you are viewing this from the -;; notmuch source distribution then you can simply run: -;; -;; sudo make install-emacs -;; -;; to install it. -;; -;; Then, to actually run it, add: -;; -;; (autoload 'notmuch "notmuch" "Notmuch mail" t) -;; -;; to your ~/.emacs file, and then run "M-x notmuch" from within emacs, -;; or run: -;; -;; emacs -f notmuch -;; -;; Have fun, and let us know if you have any comment, questions, or -;; kudos: Notmuch list <notmuch@notmuchmail.org> (subscription is not -;; required, but is available from https://notmuchmail.org). -;; -;; Note for MELPA users (and others tracking the development version -;; of notmuch-emacs): -;; -;; This emacs package needs a fairly closely matched version of the -;; notmuch program. If you use the MELPA version of notmuch.el (as -;; opposed to MELPA stable), you should be prepared to track the -;; master development branch (i.e. build from git) for the notmuch -;; program as well. Upgrading notmuch-emacs too far beyond the notmuch -;; program can CAUSE YOUR EMAIL TO STOP WORKING. -;; -;; TL;DR: notmuch-emacs from MELPA and notmuch from distro packages is -;; NOT SUPPORTED. - -;;; Code: - -(require 'mm-view) -(require 'message) - -(require 'hl-line) - -(require 'notmuch-lib) -(require 'notmuch-tag) -(require 'notmuch-show) -(require 'notmuch-tree) -(require 'notmuch-mua) -(require 'notmuch-hello) -(require 'notmuch-maildir-fcc) -(require 'notmuch-message) -(require 'notmuch-parser) - -;;; Options - -(defcustom notmuch-search-result-format - `(("date" . "%12s ") - ("count" . "%-7s ") - ("authors" . "%-20s ") - ("subject" . "%s ") - ("tags" . "(%s)")) - "Search result formatting. - -List of pairs of (field . format-string). Supported field -strings are: \"date\", \"count\", \"authors\", \"subject\", -\"tags\". It is also supported to pass a function in place of a -field name. In this case the function is passed the thread -object (plist) and format string. - -Line breaks are permitted in format strings (though this is -currently experimental). Note that a line break at the end of an -\"authors\" field will get elided if the authors list is long; -place it instead at the beginning of the following field. To -enter a line break when setting this variable with setq, use \\n. -To enter a line break in customize, press \\[quoted-insert] C-j." - :type '(alist - :key-type - (choice - (const :tag "Date" "date") - (const :tag "Count" "count") - (const :tag "Authors" "authors") - (const :tag "Subject" "subject") - (const :tag "Tags" "tags") - function) - :value-type (string :tag "Format")) - :group 'notmuch-search) - -;; The name of this variable `notmuch-init-file' is consistent with the -;; convention used in e.g. emacs and gnus. The value, `notmuch-config[.el[c]]' -;; is consistent with notmuch cli configuration file `~/.notmuch-config'. -(defcustom notmuch-init-file (locate-user-emacs-file "notmuch-config") - "Your Notmuch Emacs-Lisp configuration file name. -If a file with one of the suffixes defined by `get-load-suffixes' exists, -it will be read instead. -This file is read once when notmuch is loaded; the notmuch hooks added -there will be called at other points of notmuch execution." - :type 'file - :group 'notmuch) - -(defcustom notmuch-search-hook '(notmuch-hl-line-mode) - "List of functions to call when notmuch displays the search results." - :type 'hook - :options '(notmuch-hl-line-mode) - :group 'notmuch-search - :group 'notmuch-hooks) - -;;; Mime Utilities - -(defun notmuch-foreach-mime-part (function mm-handle) - (cond ((stringp (car mm-handle)) - (dolist (part (cdr mm-handle)) - (notmuch-foreach-mime-part function part))) - ((bufferp (car mm-handle)) - (funcall function mm-handle)) - (t (dolist (part mm-handle) - (notmuch-foreach-mime-part function part))))) - -(defun notmuch-count-attachments (mm-handle) - (let ((count 0)) - (notmuch-foreach-mime-part - (lambda (p) - (let ((disposition (mm-handle-disposition p))) - (and (listp disposition) - (or (equal (car disposition) "attachment") - (and (equal (car disposition) "inline") - (assq 'filename disposition))) - (cl-incf count)))) - mm-handle) - count)) - -(defun notmuch-save-attachments (mm-handle &optional queryp) - (notmuch-foreach-mime-part - (lambda (p) - (let ((disposition (mm-handle-disposition p))) - (and (listp disposition) - (or (equal (car disposition) "attachment") - (and (equal (car disposition) "inline") - (assq 'filename disposition))) - (or (not queryp) - (y-or-n-p - (concat "Save '" (cdr (assq 'filename disposition)) "' "))) - (mm-save-part p)))) - mm-handle)) - -;;; Keymap - -(defvar notmuch-search-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map notmuch-common-keymap) - (define-key map "x" 'notmuch-bury-or-kill-this-buffer) - (define-key map (kbd "DEL") 'notmuch-search-scroll-down) - (define-key map "b" 'notmuch-search-scroll-down) - (define-key map " " 'notmuch-search-scroll-up) - (define-key map "<" 'notmuch-search-first-thread) - (define-key map ">" 'notmuch-search-last-thread) - (define-key map "p" 'notmuch-search-previous-thread) - (define-key map "n" 'notmuch-search-next-thread) - (define-key map "r" 'notmuch-search-reply-to-thread-sender) - (define-key map "R" 'notmuch-search-reply-to-thread) - (define-key map "o" 'notmuch-search-toggle-order) - (define-key map "c" 'notmuch-search-stash-map) - (define-key map "t" 'notmuch-search-filter-by-tag) - (define-key map "l" 'notmuch-search-filter) - (define-key map "E" 'notmuch-search-edit-search) - (define-key map [mouse-1] 'notmuch-search-show-thread) - (define-key map "k" 'notmuch-tag-jump) - (define-key map "*" 'notmuch-search-tag-all) - (define-key map "a" 'notmuch-search-archive-thread) - (define-key map "-" 'notmuch-search-remove-tag) - (define-key map "+" 'notmuch-search-add-tag) - (define-key map (kbd "RET") 'notmuch-search-show-thread) - (define-key map (kbd "M-RET") 'notmuch-tree-from-search-thread) - (define-key map "Z" 'notmuch-tree-from-search-current-query) - (define-key map "U" 'notmuch-unthreaded-from-search-current-query) - map) - "Keymap for \"notmuch search\" buffers.") - -;;; Internal Variables - -(defvar notmuch-query-history nil - "Variable to store minibuffer history for notmuch queries.") - -(defvar-local notmuch-search-query-string nil) -(defvar-local notmuch-search-target-thread nil) -(defvar-local notmuch-search-target-line nil) - -;;; Stashing - -(defvar notmuch-search-stash-map - (let ((map (make-sparse-keymap))) - (define-key map "i" 'notmuch-search-stash-thread-id) - (define-key map "q" 'notmuch-stash-query) - (define-key map "?" 'notmuch-subkeymap-help) - map) - "Submap for stash commands.") -(fset 'notmuch-search-stash-map notmuch-search-stash-map) - -(defun notmuch-search-stash-thread-id () - "Copy thread ID of current thread to kill-ring." - (interactive) - (notmuch-common-do-stash (notmuch-search-find-thread-id))) - -(defun notmuch-stash-query () - "Copy current query to kill-ring." - (interactive) - (notmuch-common-do-stash notmuch-search-query-string)) - -;;; Movement - -(defun notmuch-search-scroll-up () - "Move forward through search results by one window's worth." - (interactive) - (condition-case nil - (scroll-up nil) - ((end-of-buffer) (notmuch-search-last-thread)))) - -(defun notmuch-search-scroll-down () - "Move backward through the search results by one window's worth." - (interactive) - ;; I don't know why scroll-down doesn't signal beginning-of-buffer - ;; the way that scroll-up signals end-of-buffer, but c'est la vie. - ;; - ;; So instead of trapping a signal we instead check whether the - ;; window begins on the first line of the buffer and if so, move - ;; directly to that position. (We have to count lines since the - ;; window-start position is not the same as point-min due to the - ;; invisible thread-ID characters on the first line. - (if (equal (count-lines (point-min) (window-start)) 0) - (goto-char (point-min)) - (scroll-down nil))) - -(defun notmuch-search-next-thread () - "Select the next thread in the search results." - (interactive) - (when (notmuch-search-get-result) - (goto-char (notmuch-search-result-end)))) - -(defun notmuch-search-previous-thread () - "Select the previous thread in the search results." - (interactive) - (if (notmuch-search-get-result) - (unless (bobp) - (goto-char (notmuch-search-result-beginning (- (point) 1)))) - ;; We must be past the end; jump to the last result - (notmuch-search-last-thread))) - -(defun notmuch-search-last-thread () - "Select the last thread in the search results." - (interactive) - (goto-char (point-max)) - (forward-line -2) - (let ((beg (notmuch-search-result-beginning))) - (when beg - (goto-char beg)))) - -(defun notmuch-search-first-thread () - "Select the first thread in the search results." - (interactive) - (goto-char (point-min))) - -;;; Faces - -(defface notmuch-message-summary-face - `((((class color) (background light)) - ,@(and (>= emacs-major-version 27) '(:extend t)) - :background "#f0f0f0") - (((class color) (background dark)) - ,@(and (>= emacs-major-version 27) '(:extend t)) - :background "#303030")) - "Face for the single-line message summary in notmuch-show-mode." - :group 'notmuch-show - :group 'notmuch-faces) - -(defface notmuch-search-date - '((t :inherit default)) - "Face used in search mode for dates." - :group 'notmuch-search - :group 'notmuch-faces) - -(defface notmuch-search-count - '((t :inherit default)) - "Face used in search mode for the count matching the query." - :group 'notmuch-search - :group 'notmuch-faces) - -(defface notmuch-search-subject - '((t :inherit default)) - "Face used in search mode for subjects." - :group 'notmuch-search - :group 'notmuch-faces) - -(defface notmuch-search-matching-authors - '((t :inherit default)) - "Face used in search mode for authors matching the query." - :group 'notmuch-search - :group 'notmuch-faces) - -(defface notmuch-search-non-matching-authors - '((((class color) - (background dark)) - (:foreground "grey30")) - (((class color) - (background light)) - (:foreground "grey60")) - (t - (:italic t))) - "Face used in search mode for authors not matching the query." - :group 'notmuch-search - :group 'notmuch-faces) - -(defface notmuch-tag-face - '((((class color) - (background dark)) - (:foreground "OliveDrab1")) - (((class color) - (background light)) - (:foreground "navy blue" :bold t)) - (t - (:bold t))) - "Face used in search mode face for tags." - :group 'notmuch-search - :group 'notmuch-faces) - -(defface notmuch-search-flagged-face - '((((class color) - (background dark)) - (:foreground "LightBlue1")) - (((class color) - (background light)) - (:foreground "blue"))) - "Face used in search mode face for flagged threads. - -This face is the default value for the \"flagged\" tag in -`notmuch-search-line-faces'." - :group 'notmuch-search - :group 'notmuch-faces) - -(defface notmuch-search-unread-face - '((t - (:weight bold))) - "Face used in search mode for unread threads. - -This face is the default value for the \"unread\" tag in -`notmuch-search-line-faces'." - :group 'notmuch-search - :group 'notmuch-faces) - -;;; Mode - -(define-derived-mode notmuch-search-mode fundamental-mode "notmuch-search" - "Major mode displaying results of a notmuch search. - -This buffer contains the results of a \"notmuch search\" of your -email archives. Each line in the buffer represents a single -thread giving a summary of the thread (a relative date, the -number of matched messages and total messages in the thread, -participants in the thread, a representative subject line, and -any tags). - -Pressing \\[notmuch-search-show-thread] on any line displays that -thread. The '\\[notmuch-search-add-tag]' and -'\\[notmuch-search-remove-tag]' keys can be used to add or remove -tags from a thread. The '\\[notmuch-search-archive-thread]' key -is a convenience for archiving a thread (applying changes in -`notmuch-archive-tags'). The '\\[notmuch-search-tag-all]' key can -be used to add and/or remove tags from all messages (as opposed -to threads) that match the current query. Use with caution, as -this will also tag matching messages that arrived *after* -constructing the buffer. - -Other useful commands are '\\[notmuch-search-filter]' for -filtering the current search based on an additional query string, -'\\[notmuch-search-filter-by-tag]' for filtering to include only -messages with a given tag, and '\\[notmuch-search]' to execute a -new, global search. - -Complete list of currently available key bindings: - -\\{notmuch-search-mode-map}" - (setq notmuch-buffer-refresh-function #'notmuch-search-refresh-view) - (setq-local scroll-preserve-screen-position t) - (add-to-invisibility-spec (cons 'ellipsis t)) - (setq truncate-lines t) - (setq buffer-read-only t) - (setq imenu-prev-index-position-function - #'notmuch-search-imenu-prev-index-position-function) - (setq imenu-extract-index-name-function - #'notmuch-search-imenu-extract-index-name-function)) - -;;; Search Results - -(defun notmuch-search-get-result (&optional pos) - "Return the result object for the thread at POS (or point). - -If there is no thread at POS (or point), returns nil." - (get-text-property (or pos (point)) 'notmuch-search-result)) - -(defun notmuch-search-result-beginning (&optional pos) - "Return the point at the beginning of the thread at POS (or point). - -If there is no thread at POS (or point), returns nil." - (and (notmuch-search-get-result pos) - ;; We pass 1+point because previous-single-property-change starts - ;; searching one before the position we give it. - (previous-single-property-change (1+ (or pos (point))) - 'notmuch-search-result nil - (point-min)))) - -(defun notmuch-search-result-end (&optional pos) - "Return the point at the end of the thread at POS (or point). - -The returned point will be just after the newline character that -ends the result line. If there is no thread at POS (or point), -returns nil." - (and (notmuch-search-get-result pos) - (next-single-property-change (or pos (point)) - 'notmuch-search-result nil - (point-max)))) - -(defun notmuch-search-foreach-result (beg end fn) - "Invoke FN for each result between BEG and END. - -FN should take one argument. It will be applied to the character -position of the beginning of each result that overlaps the region -between points BEG and END. As a special case, if (= BEG END), -FN will be applied to the result containing point BEG." - (let ((pos (notmuch-search-result-beginning beg)) - ;; End must be a marker in case fn changes the - ;; text. - (end (copy-marker end)) - ;; Make sure we examine at least one result, even if - ;; (= beg end). - (first t)) - ;; We have to be careful if the region extends beyond the results. - ;; In this case, pos could be null or there could be no result at - ;; pos. - (while (and pos (or (< pos end) first)) - (when (notmuch-search-get-result pos) - (funcall fn pos)) - (setq pos (notmuch-search-result-end pos)) - (setq first nil)))) -;; Unindent the function argument of notmuch-search-foreach-result so -;; the indentation of callers doesn't get out of hand. -(put 'notmuch-search-foreach-result 'lisp-indent-function 2) - -(defun notmuch-search-properties-in-region (property beg end) - (let (output) - (notmuch-search-foreach-result beg end - (lambda (pos) - (push (plist-get (notmuch-search-get-result pos) property) output))) - output)) - -(defun notmuch-search-find-thread-id (&optional bare) - "Return the thread for the current thread. - -If BARE is set then do not prefix with \"thread:\"." - (let ((thread (plist-get (notmuch-search-get-result) :thread))) - (when thread - (concat (and (not bare) "thread:") thread)))) - -(defun notmuch-search-find-stable-query () - "Return the stable queries for the current thread. - -Return a list (MATCHED-QUERY UNMATCHED-QUERY) for the -matched and unmatched messages in the current thread." - (plist-get (notmuch-search-get-result) :query)) - -(defun notmuch-search-find-stable-query-region (beg end &optional only-matched) - "Return the stable query for the current region. - -If ONLY-MATCHED is non-nil, include only matched messages. If it -is nil, include both matched and unmatched messages. If there are -no messages in the region then return nil." - (let ((query-list nil) (all (not only-matched))) - (dolist (queries (notmuch-search-properties-in-region :query beg end)) - (when (car queries) - (push (car queries) query-list)) - (when (and all (cadr queries)) - (push (cadr queries) query-list))) - (and query-list - (concat "(" (mapconcat 'identity query-list ") or (") ")")))) - -(defun notmuch-search-find-authors () - "Return the authors for the current thread." - (plist-get (notmuch-search-get-result) :authors)) - -(defun notmuch-search-find-authors-region (beg end) - "Return a list of authors for the current region." - (notmuch-search-properties-in-region :authors beg end)) - -(defun notmuch-search-find-subject () - "Return the subject for the current thread." - (plist-get (notmuch-search-get-result) :subject)) - -(defun notmuch-search-find-subject-region (beg end) - "Return a list of authors for the current region." - (notmuch-search-properties-in-region :subject beg end)) - -(defun notmuch-search-show-thread (&optional elide-toggle) - "Display the currently selected thread. - -With a prefix argument, invert the default value of -`notmuch-show-only-matching-messages' when displaying the -thread. - -Return non-nil on success." - (interactive "P") - (let ((thread-id (notmuch-search-find-thread-id))) - (if thread-id - (notmuch-show thread-id - elide-toggle - (current-buffer) - notmuch-search-query-string - ;; Name the buffer based on the subject. - (format "*%s*" (truncate-string-to-width - (notmuch-search-find-subject) - 30 nil nil t))) - (message "End of search results.") - nil))) - -(defun notmuch-tree-from-search-current-query () - "Tree view of current query." - (interactive) - (notmuch-tree notmuch-search-query-string)) - -(defun notmuch-unthreaded-from-search-current-query () - "Unthreaded view of current query." - (interactive) - (notmuch-unthreaded notmuch-search-query-string)) - -(defun notmuch-tree-from-search-thread () - "Show the selected thread with notmuch-tree." - (interactive) - (notmuch-tree (notmuch-search-find-thread-id) - notmuch-search-query-string - nil - (notmuch-prettify-subject (notmuch-search-find-subject)) - t nil (current-buffer))) - -(defun notmuch-search-reply-to-thread (&optional prompt-for-sender) - "Begin composing a reply-all to the entire current thread in a new buffer." - (interactive "P") - (notmuch-mua-new-reply (notmuch-search-find-thread-id) - prompt-for-sender t)) - -(defun notmuch-search-reply-to-thread-sender (&optional prompt-for-sender) - "Begin composing a reply to the entire current thread in a new buffer." - (interactive "P") - (notmuch-mua-new-reply (notmuch-search-find-thread-id) - prompt-for-sender nil)) - -;;; Tags - -(defun notmuch-search-set-tags (tags &optional pos) - (notmuch-search-update-result - (plist-put (notmuch-search-get-result pos) :tags tags) - pos)) - -(defun notmuch-search-get-tags (&optional pos) - (plist-get (notmuch-search-get-result pos) :tags)) - -(defun notmuch-search-get-tags-region (beg end) - (let (output) - (notmuch-search-foreach-result beg end - (lambda (pos) - (setq output (append output (notmuch-search-get-tags pos))))) - (delete-dups output))) - -(defun notmuch-search-interactive-tag-changes (&optional initial-input) - "Prompt for tag changes for the current thread or region. - -Return (TAG-CHANGES REGION-BEGIN REGION-END)." - (pcase-let ((`(,beg ,end) (notmuch-interactive-region))) - (list (notmuch-read-tag-changes (notmuch-search-get-tags-region beg end) - (if (= beg end) "Tag thread" "Tag region") - initial-input) - beg end))) - -(defun notmuch-search-tag (tag-changes &optional beg end only-matched) - "Change tags for the currently selected thread or region. - -See `notmuch-tag' for information on the format of TAG-CHANGES. -When called interactively, this uses the region if the region is -active. When called directly, BEG and END provide the region. -If these are nil or not provided, then, if the region is active -this applied to all threads meeting the region, and if the region -is inactive this applies to the thread at point. - -If ONLY-MATCHED is non-nil, only tag matched messages." - (interactive (notmuch-search-interactive-tag-changes)) - (unless (and beg end) - (setq beg (car (notmuch-interactive-region))) - (setq end (cadr (notmuch-interactive-region)))) - (let ((search-string (notmuch-search-find-stable-query-region - beg end only-matched))) - (notmuch-tag search-string tag-changes) - (notmuch-search-foreach-result beg end - (lambda (pos) - (notmuch-search-set-tags - (notmuch-update-tags (notmuch-search-get-tags pos) tag-changes) - pos))))) - -(defun notmuch-search-add-tag (tag-changes &optional beg end) - "Change tags for the current thread or region (defaulting to add). - -Same as `notmuch-search-tag' but sets initial input to '+'." - (interactive (notmuch-search-interactive-tag-changes "+")) - (notmuch-search-tag tag-changes beg end)) - -(defun notmuch-search-remove-tag (tag-changes &optional beg end) - "Change tags for the current thread or region (defaulting to remove). - -Same as `notmuch-search-tag' but sets initial input to '-'." - (interactive (notmuch-search-interactive-tag-changes "-")) - (notmuch-search-tag tag-changes beg end)) - -(put 'notmuch-search-archive-thread 'notmuch-prefix-doc - "Un-archive the currently selected thread.") -(defun notmuch-search-archive-thread (&optional unarchive beg end) - "Archive the currently selected thread or region. - -Archive each message in the currently selected thread by applying -the tag changes in `notmuch-archive-tags' to each (remove the -\"inbox\" tag by default). If a prefix argument is given, the -messages will be \"unarchived\" (i.e. the tag changes in -`notmuch-archive-tags' will be reversed). - -This function advances the next thread when finished." - (interactive (cons current-prefix-arg (notmuch-interactive-region))) - (when notmuch-archive-tags - (notmuch-search-tag - (notmuch-tag-change-list notmuch-archive-tags unarchive) beg end)) - (when (eq beg end) - (notmuch-search-next-thread))) - -;;; Search Results - -(defun notmuch-search-update-result (result &optional pos) - "Replace the result object of the thread at POS (or point) by -RESULT and redraw it. - -This will keep point in a reasonable location. However, if there -are enclosing save-excursions and the saved point is in the -result being updated, the point will be restored to the beginning -of the result." - (let ((start (notmuch-search-result-beginning pos)) - (end (notmuch-search-result-end pos)) - (init-point (point)) - (inhibit-read-only t)) - ;; Delete the current thread - (delete-region start end) - ;; Insert the updated thread - (notmuch-search-show-result result start) - ;; If point was inside the old result, make an educated guess - ;; about where to place it now. Unfortunately, this won't work - ;; with save-excursion (or any other markers that would be nice to - ;; preserve, such as the window start), but there's nothing we can - ;; do about that without a way to retrieve markers in a region. - (when (and (>= init-point start) (<= init-point end)) - (let* ((new-end (notmuch-search-result-end start)) - (new-point (if (= init-point end) - new-end - (min init-point (- new-end 1))))) - (goto-char new-point))))) - -(defun notmuch-search-process-sentinel (proc _msg) - "Add a message to let user know when \"notmuch search\" exits." - (let ((buffer (process-buffer proc)) - (status (process-status proc)) - (exit-status (process-exit-status proc)) - (never-found-target-thread nil)) - (when (memq status '(exit signal)) - (catch 'return - (kill-buffer (process-get proc 'parse-buf)) - (when (buffer-live-p buffer) - (with-current-buffer buffer - (save-excursion - (let ((inhibit-read-only t) - (atbob (bobp))) - (goto-char (point-max)) - (when (eq status 'signal) - (insert "Incomplete search results (search process was killed).\n")) - (when (eq status 'exit) - (insert "End of search results.\n") - ;; For version mismatch, there's no point in - ;; showing the search buffer - (when (or (= exit-status 20) (= exit-status 21)) - (kill-buffer) - (throw 'return nil)) - (when (and atbob - (not (string= notmuch-search-target-thread "found"))) - (setq never-found-target-thread t))))) - (when (and never-found-target-thread - notmuch-search-target-line) - (goto-char (point-min)) - (forward-line (1- notmuch-search-target-line))))))))) - -(define-widget 'notmuch--custom-face-edit 'lazy - "Custom face edit with a tag Edit Face" - ;; I could not persuage custom-face-edit to respect the :tag - ;; property so create a widget specially - :tag "Manually specify face" - :type 'custom-face-edit) - -(defcustom notmuch-search-line-faces - '(("unread" . notmuch-search-unread-face) - ("flagged" . notmuch-search-flagged-face)) - "Alist of tags to faces for line highlighting in notmuch-search. -Each element looks like (TAG . FACE). -A thread with TAG will have FACE applied. - -Here is an example of how to color search results based on tags. - (the following text would be placed in your ~/.emacs file): - - (setq notmuch-search-line-faces \\='((\"unread\" . (:foreground \"green\")) - (\"deleted\" . (:foreground \"red\" - :background \"blue\")))) - -The FACE must be a face name (a symbol or string), a property -list of face attributes, or a list of these. The faces for -matching tags are merged, with earlier attributes overriding -later. A message having both \"deleted\" and \"unread\" tags with -the above settings would have a green foreground and blue -background." - :type '(alist :key-type (string) - :value-type (radio (face :tag "Face name") - (notmuch--custom-face-edit))) - :group 'notmuch-search - :group 'notmuch-faces) - -(defun notmuch-search-color-line (start end line-tag-list) - "Colorize lines in `notmuch-show' based on tags." - ;; Reverse the list so earlier entries take precedence - (dolist (elem (reverse notmuch-search-line-faces)) - (let ((tag (car elem)) - (face (cdr elem))) - (when (member tag line-tag-list) - (notmuch-apply-face nil face nil start end))))) - -(defun notmuch-search-author-propertize (authors) - "Split `authors' into matching and non-matching authors and -propertize appropriately. If no boundary between authors and -non-authors is found, assume that all of the authors match." - (if (string-match "\\(.*\\)|\\(.*\\)" authors) - (concat (propertize (concat (match-string 1 authors) ",") - 'face 'notmuch-search-matching-authors) - (propertize (match-string 2 authors) - 'face 'notmuch-search-non-matching-authors)) - (propertize authors 'face 'notmuch-search-matching-authors))) - -(defun notmuch-search-insert-authors (format-string authors) - ;; Save the match data to avoid interfering with - ;; `notmuch-search-process-filter'. - (save-match-data - (let* ((formatted-authors (format format-string authors)) - (formatted-sample (format format-string "")) - (visible-string formatted-authors) - (invisible-string "") - (padding "")) - ;; Truncate the author string to fit the specification. - (when (> (length formatted-authors) - (length formatted-sample)) - (let ((visible-length (- (length formatted-sample) - (length "... ")))) - ;; Truncate the visible string according to the width of - ;; the display string. - (setq visible-string (substring formatted-authors 0 visible-length)) - (setq invisible-string (substring formatted-authors visible-length)) - ;; If possible, truncate the visible string at a natural - ;; break (comma or pipe), as incremental search doesn't - ;; match across the visible/invisible border. - (when (string-match "\\(.*\\)\\([,|] \\)\\([^,|]*\\)" visible-string) - ;; Second clause is destructive on `visible-string', so - ;; order is important. - (setq invisible-string (concat (match-string 3 visible-string) - invisible-string)) - (setq visible-string (concat (match-string 1 visible-string) - (match-string 2 visible-string)))) - ;; `visible-string' may be shorter than the space allowed - ;; by `format-string'. If so we must insert some padding - ;; after `invisible-string'. - (setq padding (make-string (- (length formatted-sample) - (length visible-string) - (length "...")) - ? )))) - ;; Use different faces to show matching and non-matching authors. - (if (string-match "\\(.*\\)|\\(.*\\)" visible-string) - ;; The visible string contains both matching and - ;; non-matching authors. - (progn - (setq visible-string (notmuch-search-author-propertize visible-string)) - ;; The invisible string must contain only non-matching - ;; authors, as the visible-string contains both. - (setq invisible-string (propertize invisible-string - 'face 'notmuch-search-non-matching-authors))) - ;; The visible string contains only matching authors. - (setq visible-string (propertize visible-string - 'face 'notmuch-search-matching-authors)) - ;; The invisible string may contain both matching and - ;; non-matching authors. - (setq invisible-string (notmuch-search-author-propertize invisible-string))) - ;; If there is any invisible text, add it as a tooltip to the - ;; visible text. - (unless (string-empty-p invisible-string) - (setq visible-string - (propertize visible-string - 'help-echo (concat "..." invisible-string)))) - ;; Insert the visible and, if present, invisible author strings. - (insert visible-string) - (unless (string-empty-p invisible-string) - (let ((start (point)) - overlay) - (insert invisible-string) - (setq overlay (make-overlay start (point))) - (overlay-put overlay 'evaporate t) - (overlay-put overlay 'invisible 'ellipsis) - (overlay-put overlay 'isearch-open-invisible #'delete-overlay))) - (insert padding)))) - -(defun notmuch-search-insert-field (field format-string result) - (pcase field - ((pred functionp) - (insert (funcall field format-string result))) - ("date" - (insert (propertize (format format-string (plist-get result :date_relative)) - 'face 'notmuch-search-date))) - ("count" - (insert (propertize (format format-string - (format "[%s/%s]" (plist-get result :matched) - (plist-get result :total))) - 'face 'notmuch-search-count))) - ("subject" - (insert (propertize (format format-string - (notmuch-sanitize (plist-get result :subject))) - 'face 'notmuch-search-subject))) - ("authors" - (notmuch-search-insert-authors format-string - (notmuch-sanitize (plist-get result :authors)))) - ("tags" - (let ((tags (plist-get result :tags)) - (orig-tags (plist-get result :orig-tags))) - (insert (format format-string (notmuch-tag-format-tags tags orig-tags))))))) - -(defun notmuch-search-show-result (result pos) - "Insert RESULT at POS." - ;; Ignore excluded matches - (unless (= (plist-get result :matched) 0) - (save-excursion - (goto-char pos) - (dolist (spec notmuch-search-result-format) - (notmuch-search-insert-field (car spec) (cdr spec) result)) - (insert "\n") - (notmuch-search-color-line pos (point) (plist-get result :tags)) - (put-text-property pos (point) 'notmuch-search-result result)))) - -(defun notmuch-search-append-result (result) - "Insert RESULT at the end of the buffer. - -This is only called when a result is first inserted so it also -sets the :orig-tag property." - (let ((new-result (plist-put result :orig-tags (plist-get result :tags))) - (pos (point-max))) - (notmuch-search-show-result new-result pos) - (when (string= (plist-get result :thread) notmuch-search-target-thread) - (setq notmuch-search-target-thread "found") - (goto-char pos)))) - -(defvar-local notmuch--search-hook-run nil - "Flag used to ensure the notmuch-search-hook is only run once per buffer") - -(defun notmuch--search-hook-wrapper () - (unless notmuch--search-hook-run - (setq notmuch--search-hook-run t) - (run-hooks 'notmuch-search-hook))) - -(defun notmuch-search-process-filter (proc string) - "Process and filter the output of \"notmuch search\"." - (let ((results-buf (process-buffer proc)) - (parse-buf (process-get proc 'parse-buf)) - (inhibit-read-only t)) - (when (buffer-live-p results-buf) - (with-current-buffer parse-buf - ;; Insert new data - (save-excursion - (goto-char (point-max)) - (insert string)) - (notmuch-sexp-parse-partial-list 'notmuch-search-append-result - results-buf)) - (with-current-buffer results-buf - (notmuch--search-hook-wrapper))))) - -;;; Commands (and some helper functions used by them) - -(defun notmuch-search-tag-all (tag-changes) - "Add/remove tags from all messages in current search buffer. - -See `notmuch-tag' for information on the format of TAG-CHANGES." - (interactive - (list (notmuch-read-tag-changes - (notmuch-search-get-tags-region (point-min) (point-max)) "Tag all"))) - (notmuch-search-tag tag-changes (point-min) (point-max) t)) - -(defcustom notmuch-search-buffer-name-format "*notmuch-%t-%s*" - "Format for the name of search results buffers. - -In this spec, %s will be replaced by a description of the search -query and %t by its type (search, tree or unthreaded). The -buffer name is formatted using `format-spec': see its docstring -for additional parameters for the s and t format specifiers. - -See also `notmuch-saved-search-buffer-name-format'" - :type 'string - :group 'notmuch-search) - -(defcustom notmuch-saved-search-buffer-name-format "*notmuch-saved-%t-%s*" - "Format for the name of search results buffers for saved searches. - -In this spec, %s will be replaced by the saved search name and %t -by its type (search, tree or unthreaded). The buffer name is -formatted using `format-spec': see its docstring for additional -parameters for the s and t format specifiers. - -See also `notmuch-search-buffer-name-format'" - :type 'string - :group 'notmuch-search) - -(defun notmuch-search-format-buffer-name (query type saved) - "Compose a buffer name for the given QUERY, TYPE (search, tree, -unthreaded) and whether it's SAVED (t or nil)." - (let ((fmt (if saved - notmuch-saved-search-buffer-name-format - notmuch-search-buffer-name-format))) - (format-spec fmt `((?t . ,(or type "search")) (?s . ,query))))) - -(defun notmuch-search-buffer-title (query &optional type) - "Returns the title for a buffer with notmuch search results." - (let* ((saved-search - (let (longest - (longest-length 0)) - (cl-loop for tuple in notmuch-saved-searches - if (let ((quoted-query - (regexp-quote - (notmuch-saved-search-get tuple :query)))) - (and (string-match (concat "^" quoted-query) query) - (> (length (match-string 0 query)) - longest-length))) - do (setq longest tuple)) - longest)) - (saved-search-name (notmuch-saved-search-get saved-search :name)) - (saved-search-type (notmuch-saved-search-get saved-search :search-type)) - (saved-search-query (notmuch-saved-search-get saved-search :query))) - (cond ((and saved-search (equal saved-search-query query)) - ;; Query is the same as saved search (ignoring case) - (notmuch-search-format-buffer-name saved-search-name - saved-search-type - t)) - (saved-search - (let ((query (replace-regexp-in-string - (concat "^" (regexp-quote saved-search-query)) - (concat "[ " saved-search-name " ]") - query))) - (notmuch-search-format-buffer-name query saved-search-type t))) - (t (notmuch-search-format-buffer-name query type nil))))) - -(defun notmuch-read-query (prompt) - "Read a notmuch-query from the minibuffer with completion. - -PROMPT is the string to prompt with." - (let* ((all-tags - (mapcar (lambda (tag) (notmuch-escape-boolean-term tag)) - (notmuch--process-lines notmuch-command "search" "--output=tags" "*"))) - (completions - (append (list "folder:" "path:" "thread:" "id:" "date:" "from:" "to:" - "subject:" "attachment:") - (mapcar (lambda (tag) (concat "tag:" tag)) all-tags) - (mapcar (lambda (tag) (concat "is:" tag)) all-tags) - (mapcar (lambda (mimetype) (concat "mimetype:" mimetype)) - (mailcap-mime-types)))) - (keymap (copy-keymap minibuffer-local-map)) - (current-query (cl-case major-mode - (notmuch-search-mode (notmuch-search-get-query)) - (notmuch-show-mode (notmuch-show-get-query)) - (notmuch-tree-mode (notmuch-tree-get-query)))) - (minibuffer-completion-table - (completion-table-dynamic - (lambda (string) - ;; Generate a list of possible completions for the current input. - (cond - ;; This ugly regexp is used to get the last word of the input - ;; possibly preceded by a '('. - ((string-match "\\(^\\|.* (?\\)\\([^ ]*\\)$" string) - (mapcar (lambda (compl) - (concat (match-string-no-properties 1 string) compl)) - (all-completions (match-string-no-properties 2 string) - completions))) - (t (list string))))))) - ;; This was simpler than convincing completing-read to accept spaces: - (define-key keymap (kbd "TAB") 'minibuffer-complete) - (let ((history-delete-duplicates t)) - (read-from-minibuffer prompt nil keymap nil - 'notmuch-search-history current-query nil)))) - -(defun notmuch-search-get-query () - "Return the current query in this search buffer." - notmuch-search-query-string) - -(put 'notmuch-search 'notmuch-doc "Search for messages.") -;;;###autoload -(defun notmuch-search (&optional query oldest-first target-thread target-line - no-display) - "Display threads matching QUERY in a notmuch-search buffer. - -If QUERY is nil, it is read interactively from the minibuffer. -Other optional parameters are used as follows: - - OLDEST-FIRST: A Boolean controlling the sort order of returned threads - TARGET-THREAD: A thread ID (without the thread: prefix) that will be made - current if it appears in the search results. - TARGET-LINE: The line number to move to if the target thread does not - appear in the search results. - NO-DISPLAY: Do not try to foreground the search results buffer. If it is - already foregrounded i.e. displayed in a window, this has no - effect, meaning the buffer will remain visible. - -When called interactively, this will prompt for a query and use -the configured default sort order." - (interactive - (list - ;; Prompt for a query - nil - ;; Use the default search order (if we're doing a search from a - ;; search buffer, ignore any buffer-local overrides) - (default-value 'notmuch-search-oldest-first))) - - (let* ((query (or query (notmuch-read-query "Notmuch search: "))) - (buffer (get-buffer-create (notmuch-search-buffer-title query)))) - (if no-display - (set-buffer buffer) - (pop-to-buffer-same-window buffer)) - (notmuch-search-mode) - ;; Don't track undo information for this buffer - (setq buffer-undo-list t) - (setq notmuch-search-query-string query) - (setq notmuch-search-oldest-first oldest-first) - (setq notmuch-search-target-thread target-thread) - (setq notmuch-search-target-line target-line) - (notmuch-tag-clear-cache) - (when (get-buffer-process buffer) - (error "notmuch search process already running for query `%s'" query)) - (let ((inhibit-read-only t)) - (erase-buffer) - (goto-char (point-min)) - (save-excursion - (let ((proc (notmuch-start-notmuch - "notmuch-search" buffer #'notmuch-search-process-sentinel - "search" "--format=sexp" "--format-version=5" - (if oldest-first - "--sort=oldest-first" - "--sort=newest-first") - query))) - ;; Use a scratch buffer to accumulate partial output. - ;; This buffer will be killed by the sentinel, which - ;; should be called no matter how the process dies. - (process-put proc 'parse-buf - (generate-new-buffer " *notmuch search parse*")) - (set-process-filter proc 'notmuch-search-process-filter) - (set-process-query-on-exit-flag proc nil)))))) - -(defun notmuch-search-refresh-view () - "Refresh the current view. - -Erases the current buffer and runs a new search with the same -query string as the current search. If the current thread is in -the new search results, then point will be placed on the same -thread. Otherwise, point will be moved to attempt to be in the -same relative position within the new buffer." - (interactive) - (notmuch-search notmuch-search-query-string - notmuch-search-oldest-first - (notmuch-search-find-thread-id 'bare) - (line-number-at-pos) - t) - (goto-char (point-min))) - -(defun notmuch-search-toggle-order () - "Toggle the current search order. - -This command toggles the sort order for the current search. The -default sort order is defined by `notmuch-search-oldest-first'." - (interactive) - (setq notmuch-search-oldest-first (not notmuch-search-oldest-first)) - (notmuch-search-refresh-view)) - -(defun notmuch-group-disjunctive-query-string (query-string) - "Group query if it contains a complex expression. -Enclose QUERY-STRING in parentheses if contains \"OR\" operators." - (if (string-match-p "\\<[oO][rR]\\>" query-string) - (concat "( " query-string " )") - query-string)) - -(defun notmuch-search-filter (query) - "Filter or LIMIT the current search results based on an additional query string. - -Runs a new search matching only messages that match both the -current search results AND the additional query string provided." - (interactive (list (notmuch-read-query "Filter search: "))) - (let ((grouped-query (notmuch-group-disjunctive-query-string query)) - (grouped-original-query (notmuch-group-disjunctive-query-string - notmuch-search-query-string))) - (notmuch-search (if (string= grouped-original-query "*") - grouped-query - (concat grouped-original-query " and " grouped-query)) - notmuch-search-oldest-first))) - -(defun notmuch-search-filter-by-tag (tag) - "Filter the current search results based on a single TAG. - -Run a new search matching only messages that match the current -search results and that are also tagged with the given TAG." - (interactive - (list (notmuch-select-tag-with-completion "Filter by tag: " - notmuch-search-query-string))) - (notmuch-search (concat notmuch-search-query-string " and tag:" tag) - notmuch-search-oldest-first)) - -(defun notmuch-search-by-tag (tag) - "Display threads matching TAG in a notmuch-search buffer." - (interactive - (list (notmuch-select-tag-with-completion "Notmuch search tag: "))) - (notmuch-search (concat "tag:" tag))) - -(defun notmuch-search-edit-search (query) - "Edit the current search" - (interactive (list (read-from-minibuffer "Edit search: " - notmuch-search-query-string))) - (notmuch-search query notmuch-search-oldest-first)) - -;;;###autoload -(defun notmuch () - "Run notmuch and display saved searches, known tags, etc." - (interactive) - (notmuch-hello)) - -(defun notmuch-interesting-buffer (b) - "Whether the current buffer's major-mode is a notmuch mode." - (with-current-buffer b - (memq major-mode '(notmuch-show-mode - notmuch-search-mode - notmuch-tree-mode - notmuch-hello-mode - notmuch-message-mode)))) - -;;;###autoload -(defun notmuch-cycle-notmuch-buffers () - "Cycle through any existing notmuch buffers (search, show or hello). - -If the current buffer is the only notmuch buffer, bury it. -If no notmuch buffers exist, run `notmuch'." - (interactive) - (let (start first) - ;; If the current buffer is a notmuch buffer, remember it and then - ;; bury it. - (when (notmuch-interesting-buffer (current-buffer)) - (setq start (current-buffer)) - (bury-buffer)) - - ;; Find the first notmuch buffer. - (setq first (cl-loop for buffer in (buffer-list) - if (notmuch-interesting-buffer buffer) - return buffer)) - - (if first - ;; If the first one we found is any other than the starting - ;; buffer, switch to it. - (unless (eq first start) - (pop-to-buffer-same-window first)) - (notmuch)))) - -;;; Integrations -;;;; Hl-line Support - -(defun notmuch-hl-line-mode () - (prog1 (hl-line-mode) - (when hl-line-overlay - (overlay-put hl-line-overlay 'priority 1)))) - -;;;; Imenu Support - -(defun notmuch-search-imenu-prev-index-position-function () - "Move point to previous message in notmuch-search buffer. -Used as`imenu-prev-index-position-function' in notmuch buffers." - (notmuch-search-previous-thread)) - -(defun notmuch-search-imenu-extract-index-name-function () - "Return imenu name for line at point. -Used as `imenu-extract-index-name-function' in notmuch buffers. -Point should be at the beginning of the line." - (format "%s (%s)" - (notmuch-search-find-subject) - (notmuch-search-find-authors))) - -;;; _ - -(provide 'notmuch) - -;; After provide to avoid loops if notmuch was require'd via notmuch-init-file. -(when init-file-user ; don't load init file if the -q option was used. - (load notmuch-init-file t t nil t)) - -;;; notmuch.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/notmuch.elc b/emacs/elpa/notmuch-20231006.2337/notmuch.elc Binary files differ. diff --git a/emacs/elpa/notmuch-20231006.2337/rstdoc.el b/emacs/elpa/notmuch-20231006.2337/rstdoc.el @@ -1,90 +0,0 @@ -;;; rstdoc.el --- help generate documentation from docstrings -*- lexical-binding: t -*- - -;; Copyright (C) 2018 David Bremner - -;; Author: David Bremner <david@tethera.net> -;; Created: 26 May 2018 -;; Keywords: emacs lisp, documentation -;; Homepage: https://notmuchmail.org - -;; This file is not part of GNU Emacs. - -;; rstdoc.el 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. -;; -;; rstdoc.el 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 rstdoc.el. If not, see <https://www.gnu.org/licenses/>. -;; - -;;; Commentary: - -;; Rstdoc provides a facility to extract all of the docstrings defined in -;; an elisp source file. Usage: -;; -;; emacs -Q --batch -L . -l rstdoc -f rstdoc-batch-extract foo.el foo.rsti - -;;; Code: - -(defun rstdoc-batch-extract () - "Extract docstrings to and from the files on the command line." - (apply #'rstdoc-extract command-line-args-left)) - -(defun rstdoc-extract (in-file out-file) - "Write docstrings from IN-FILE to OUT-FILE." - (load-file in-file) - (let* ((definitions (cdr (assoc (expand-file-name in-file) load-history))) - (text-quoting-style 'grave) - (doc-hash (make-hash-table :test 'eq))) - (mapc - (lambda (elt) - (let ((pair - (pcase elt - (`(defun . ,name) (cons name (documentation name))) - (`(,_ . ,_) nil) - (sym (cons sym (get sym 'variable-documentation)))))) - (when (and pair (cdr pair)) - (puthash (car pair) (cdr pair) doc-hash)))) - definitions) - (with-temp-buffer - (maphash - (lambda (key val) - (rstdoc--insert-docstring key val)) - doc-hash) - (write-region (point-min) (point-max) out-file)))) - -(defun rstdoc--insert-docstring (symbol docstring) - (insert (format "\n.. |docstring::%s| replace::\n" symbol)) - (insert (replace-regexp-in-string "^" " " - (rstdoc--rst-quote-string docstring))) - (insert "\n")) - -(defvar rst--escape-alist - '( ("\\\\='" . "\001") - ("`\\([^\n`']*\\)[`']" . "\002\\1\002") ;; good enough for now... - ("`" . "\\\\`") - ("\001" . "'") - ("\002" . "`") - ("[*]" . "\\\\*") - ("^[[:space:]]*$" . "|br|") - ("^[[:space:]]" . "|indent| ")) - "list of (regex . replacement) pairs") - -(defun rstdoc--rst-quote-string (str) - (with-temp-buffer - (insert str) - (dolist (pair rst--escape-alist) - (goto-char (point-min)) - (while (re-search-forward (car pair) nil t) - (replace-match (cdr pair)))) - (buffer-substring (point-min) (point-max)))) - -(provide 'rstdoc) - -;;; rstdoc.el ends here diff --git a/emacs/elpa/notmuch-20231006.2337/rstdoc.elc b/emacs/elpa/notmuch-20231006.2337/rstdoc.elc Binary files differ.