commit 069c0598ea2aa00b4c6bf18578bf133455f42b7a parent fa035ad370774ac9053d6d55ef3c75122ee4fb32 Author: dwrz <dwrz@dwrz.net> Date: Wed, 14 Aug 2024 12:06:38 +0000 Update Emacs packages Diffstat:
161 files changed, 8565 insertions(+), 8535 deletions(-)
diff --git a/emacs/elpa/archives/gnu/archive-contents b/emacs/elpa/archives/gnu/archive-contents @@ -981,17 +981,17 @@ ("Protesilaos Stavrou" . "info@protesilaos.com")) (:commit . "75312084112aa8ade19eb98d2ea3dbf6e16434cb"))]) (denote-menu . - [(1 2 0) + [(1 3 0) ((emacs (28 1)) (denote - (2 0 0))) + (3 0 0))) "View denote files in a tabulated list." tar ((:url . "https://github.com/namilus/denote-menu") (:maintainer "Mohamed Suliman" . "sulimanm@tcd.ie") (:authors ("Mohamed Suliman" . "sulimanm@tcd.ie")) - (:commit . "6d97b6be0511420dca27b294844bdaa5fa72f753"))]) + (:commit . "d3b890648404eb4df2dabbb3d783105672e0ea23"))]) (detached . [(0 10 1) ((emacs @@ -1275,7 +1275,7 @@ ("Markus Triska" . "triska@metalevel.at")) (:commit . "cfcdf9e42821d246b7fbc84877aa4ecacc184a1c"))]) (eev . - [(20240803) + [(20240811) ((emacs (25 1))) "Support for e-scripts (eepitch blocks, elisp hyperlinks, etc)" tar @@ -1284,7 +1284,7 @@ (:maintainer "Eduardo Ochs" . "eduardoochs@gmail.com") (:authors ("Eduardo Ochs" . "eduardoochs@gmail.com")) - (:commit . "2cac1d27a18ed6a77f43f19e87df662e344983b5"))]) + (:commit . "39f6dfdd1d38e169469ff56ac93a0881a3fe1c8b"))]) (ef-themes . [(1 7 0) ((emacs @@ -3291,6 +3291,32 @@ (:authors ("Adam Porter" . "adam@alphapapa.net")) (:commit . "399ad3e1aafa7b86c1ce0cfab8bcf2e09cb0e956"))]) + (plz-event-source . + [(0 1 1) + ((emacs + (26 3)) + (plz-media-type + (0 1 0))) + "Plz Event Source" tar + ((:url . "https://github.com/r0man/plz-event-source") + (:keywords "comm" "network" "http") + (:maintainer "r0man" . "roman@burningswell.com") + (:authors + ("r0man" . "roman@burningswell.com")) + (:commit . "09e650c8667d0b43be84fb58d9116a739a058bef"))]) + (plz-media-type . + [(0 2 1) + ((emacs + (26 3)) + (plz + (0 9))) + "Plz Media Types" tar + ((:url . "https://github.com/r0man/plz-media-type") + (:keywords "comm" "network" "http") + (:maintainer "r0man" . "roman@burningswell.com") + (:authors + ("r0man" . "roman@burningswell.com")) + (:commit . "a256633ef3e1aa0e1e43f6122e167430897ca790"))]) (plz-see . [(0 1) ((emacs @@ -4039,16 +4065,16 @@ ("Christian Johansson" . "christian@cvj.se")) (:commit . "95fb076c9b657c5f1bfad3ee5bf1f8691c50d428"))]) (standard-themes . - [(2 0 1) + [(2 1 0) ((emacs (27 1))) "Like the default theme but more consistent" tar - ((:url . "https://git.sr.ht/~protesilaos/standard-themes") + ((:url . "https://github.com/protesilaos/standard-themes") (:keywords "faces" "theme" "accessibility") - (:maintainer "Standard-Themes Development" . "~protesilaos/standard-themes@lists.sr.ht") + (:maintainer "Protesilaos Stavrou" . "info@protesilaos.com") (:authors ("Protesilaos Stavrou" . "info@protesilaos.com")) - (:commit . "4b9e5395d5faad8d831727bc37d2551d68888555"))]) + (:commit . "9a982c4fad9a6d1a68e211bd83342bdd47dd7979"))]) (stream . [(2 3 0) ((emacs @@ -4893,14 +4919,14 @@ (:url . "https://elpa.gnu.org/packages/wpuzzle.html") (:commit . "9373cbc013b978b31e38822bfdcc9a7fc5e0ed99"))]) (wrap-search . - [(4 16 17) + [(4 16 19) nil "wrapped, non-incremental search" tar ((:url . "https://dataswamp.org/~incal/elpa/wrap-search.el") (:keywords "matching") (:maintainer "Emanuel Berg" . "incal@dataswamp.org") (:authors ("Emanuel Berg" . "incal@dataswamp.org")) - (:commit . "4e913585afc59dffa56d39c4511e31552be4e80e"))]) + (:commit . "6e4bc3d6258ec367e765a6ab7051f2f2b00f1ac9"))]) (xclip . [(1 11) nil "Copy&paste GUI clipboard from text terminal" tar diff --git a/emacs/elpa/archives/gnu/archive-contents.signed b/emacs/elpa/archives/gnu/archive-contents.signed @@ -1 +1 @@ -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) <elpasign@elpa.gnu.org> (trust undefined) created at 2024-08-10T09:05:01+0000 using EDDSA -\ No newline at end of file +Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) <elpasign@elpa.gnu.org> (trust undefined) created at 2024-08-14T09:05:01+0000 using EDDSA +\ No newline at end of file diff --git a/emacs/elpa/archives/melpa/archive-contents b/emacs/elpa/archives/melpa/archive-contents @@ -187,7 +187,7 @@ (astro-ts-mode . [(20240724 332) ((emacs (29))) "Major mode for editing Astro templates" tar ((:commit . "78e7e942011839bd4f4de0a1d8460f5879ba4ca5") (:authors ("Ruby Iris Juric" . "ruby@srxl.me")) (:maintainers ("Ruby Iris Juric" . "ruby@srxl.me")) (:maintainer "Ruby Iris Juric" . "ruby@srxl.me") (:keywords "languages") (:url . "https://github.com/Sorixelle/astro-ts-mode"))]) (astyle . [(20200328 616) ((emacs (24 4)) (reformatter (0 3))) "Astyle formatter functions" tar ((:commit . "04ff2941f08c4b731fe6a18ee1697436d1ca1cc0") (:keywords "astyle" "c" "c++" "cpp" "reformatter") (:url . "https://github.com/storvik/emacs-astyle"))]) (asx . [(20191024 1100) ((emacs (26 1))) "Ask StackExchange/StackOverflow" tar ((:commit . "5ca12cc51bb02b5926adf9a7976ba9ca08a1ea21") (:authors ("Alex Ragone" . "ragonedk@gmail.com")) (:maintainers ("Alex Ragone" . "ragonedk@gmail.com")) (:maintainer "Alex Ragone" . "ragonedk@gmail.com") (:keywords "convenience") (:url . "https://github.com/ragone/asx"))]) - (async . [(20240808 645) ((emacs (24 4))) "Asynchronous processing in Emacs" tar ((:commit . "67613344e153009ac9afb96b455130b7d27ba361") (:authors ("John Wiegley" . "jwiegley@gmail.com")) (:maintainers ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainer "Thierry Volpiatto" . "thievol@posteo.net") (:keywords "async") (:url . "https://github.com/jwiegley/emacs-async"))]) + (async . [(20240811 1434) ((emacs (24 4))) "Asynchronous processing in Emacs" tar ((:commit . "e1d46f97a56e0c57206bd1ea31d04311097a8cbb") (:authors ("John Wiegley" . "jwiegley@gmail.com")) (:maintainers ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainer "Thierry Volpiatto" . "thievol@posteo.net") (:keywords "async") (:url . "https://github.com/jwiegley/emacs-async"))]) (async-await . [(20220827 437) ((emacs (25 1)) (promise (1 1)) (iter2 (0 9 10))) "Async/Await" tar ((:commit . "e0d15e8057ed7520100bc50c5552278292ebcb07") (:authors ("chuntaro" . "chuntaro@sakura-games.jp")) (:maintainers ("chuntaro" . "chuntaro@sakura-games.jp")) (:maintainer "chuntaro" . "chuntaro@sakura-games.jp") (:keywords "async" "await" "convenience") (:url . "https://github.com/chuntaro/emacs-async-await"))]) (async-backup . [(20230412 1534) ((emacs (24 4))) "Backup on each save without freezing Emacs" tar ((:commit . "d07a7bd4a5c3332a8a585680d67925385c595927") (:authors ("contrapunctus" . "xmpp:contrapunctus@jabjab.de")) (:maintainers ("contrapunctus" . "xmpp:contrapunctus@jabjab.de")) (:maintainer "contrapunctus" . "xmpp:contrapunctus@jabjab.de") (:keywords "files") (:url . "https://codeberg.org/contrapunctus/async-backup"))]) (async-job-queue . [(20230427 2122) ((async (1 4)) (emacs (25 1)) (queue (0 2))) "Dispatch queue of async jobs to a fixed number of slots" tar ((:commit . "eeafcce7f960305666b2a51aec55cc6333f6af1b") (:keywords "extensions" "lisp") (:url . "https://github.com/owinebar/emacs-async-job-queue"))]) @@ -337,7 +337,7 @@ (bibtex-capf . [(20240122 1558) ((emacs (27 1)) (parsebib (3 0)) (org (9 5))) "Completion at point for bibtex" tar ((:commit . "31826efefcbbdebdb700a06b5070df0f06ce2291") (:keywords "bibtex" "convenience") (:url . "https://github.com/mclear-tools/bibtex-capf"))]) (bibtex-completion . [(20240220 1216) ((parsebib (1 0)) (s (1 9 0)) (dash (2 6 0)) (f (0 16 2)) (cl-lib (0 5)) (biblio (0 2)) (emacs (26 1))) "A BibTeX backend for completion frameworks" tar ((:commit . "8b71b4f5ce62eeaf18067f57faaddc06449fbe1c") (:authors ("Titus von der Malsburg" . "malsburg@posteo.de") ("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"))]) (bibtex-utils . [(20190703 2117) nil "Provides utilities for extending BibTeX mode" tar ((:commit . "26a8f0909b6adbf545a2b5e57ce7f779bf7a65af") (:authors ("Tyler Smith" . "tyler@plantarum.ca")) (:maintainers ("Tyler Smith" . "tyler@plantarum.ca")) (:maintainer "Tyler Smith" . "tyler@plantarum.ca") (:keywords "bibtex") (:url . "https://github.com/plantarum/bibtex-utils"))]) - (bicycle . [(20240805 1443) ((emacs (26 1)) (compat (30 0 0 0))) "Cycle outline and code visibility" tar ((:commit . "2f1f037af123df2b8c971f0137da87e55813eefa") (:authors ("Jonas Bernoulli" . "emacs.bicycle@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.bicycle@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.bicycle@jonas.bernoulli.dev") (:keywords "outlines") (:url . "https://github.com/tarsius/bicycle"))]) + (bicycle . [(20240810 2050) ((emacs (26 1)) (compat (30 0 0 0))) "Cycle outline and code visibility" tar ((:commit . "80858c93ba5e24b89f1ccc6e4b21548f3eec35ae") (:authors ("Jonas Bernoulli" . "emacs.bicycle@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.bicycle@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.bicycle@jonas.bernoulli.dev") (:keywords "outlines") (:url . "https://github.com/tarsius/bicycle"))]) (bifocal . [(20200325 539) ((emacs (24 4))) "Split-screen scrolling for comint-mode buffers" tar ((:commit . "773a6dde790c4a240e643a9071e4c7bce09d40de") (:keywords "frames" "processes") (:url . "https://github.com/riscy/bifocal-mode"))]) (binclock . [(20170802 1116) ((cl-lib (0 5))) "Display the current time using a binary clock." tar ((:commit . "87042230d7f3fe3e9a77fae0dbab7d8f7e7794ad") (:authors ("Dave Pearson" . "davep@davep.org")) (:maintainers ("Dave Pearson" . "davep@davep.org")) (:maintainer "Dave Pearson" . "davep@davep.org") (:keywords "games" "time" "display") (:url . "https://github.com/davep/binclock.el"))]) (bind . [(20231001 2051) ((emacs (25 1))) "Bind commands to keys" tar ((:commit . "4c1698a7c1c9f3d45559c3be871d87d76a1cbe00") (:authors ("repelliuss" . "https://github.com/repelliuss")) (:maintainers ("repelliuss" . "repelliuss@gmail.com")) (:maintainer "repelliuss" . "repelliuss@gmail.com") (:url . "https://github.com/repelliuss/bind"))]) @@ -490,8 +490,8 @@ (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-avy . [(20240714 202) ((emacs (29 1)) (avy (0 5 0)) (casual-lib (1 1 0))) "Transient UI for Avy" tar ((:commit . "0f9072dc98b1af9200cf03c15b776cbd901333fc") (:authors ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainers ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainer "Charles Choi" . "kickingvegas@gmail.com") (:keywords "tools") (:url . "https://github.com/kickingvegas/casual-avy"))]) (casual-bookmarks . [(20240806 2233) ((emacs (29 1)) (casual-lib (1 1 0))) "Transient UI for Bookmarks" tar ((:commit . "d105638d9440581c4557a76f4d042a64b34e10ab") (: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-bookmarks"))]) - (casual-calc . [(20240810 1806) ((emacs (29 1)) (casual-lib (1 1 0))) "Transient UI for Calc" tar ((:commit . "6b25cba5ca2a75656b45ba4cc5c6ea3bee72ba8e") (: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-calc"))]) - (casual-dired . [(20240716 2120) ((emacs (29 1)) (casual-lib (1 1 0))) "Transient UI for Dired" tar ((:commit . "4be72b52f91700cdb529a185b8f6f21bd0a86542") (:authors ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainers ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainer "Charles Choi" . "kickingvegas@gmail.com") (:keywords "tools") (:url . "https://github.com/kickingvegas/casual-dired"))]) + (casual-calc . [(20240811 103) ((emacs (29 1)) (casual-lib (1 1 0))) "Transient UI for Calc" tar ((:commit . "a304f1ad086fad8cd78087f6b541940bf36d20d0") (: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-calc"))]) + (casual-dired . [(20240811 2038) ((emacs (29 1)) (casual-lib (1 1 0))) "Transient UI for Dired" tar ((:commit . "9f0b49035c8bf16e0b3c1869709ce4a86fc2f31c") (:authors ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainers ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainer "Charles Choi" . "kickingvegas@gmail.com") (:keywords "tools") (:url . "https://github.com/kickingvegas/casual-dired"))]) (casual-ibuffer . [(20240730 329) ((emacs (29 1)) (casual-lib (1 1 0))) "Transient UI for IBuffer" tar ((:commit . "877bffe4e69f2715f5f84ad15ca54f4a14493b80") (: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-ibuffer"))]) (casual-info . [(20240711 712) ((emacs (29 1)) (casual-lib (1 1 0))) "Transient UI for Info" tar ((:commit . "23f970d58e5f3c2dc9fa291bdce5f9e560fb205c") (:authors ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainers ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainer "Charles Choi" . "kickingvegas@gmail.com") (:keywords "tools") (:url . "https://github.com/kickingvegas/casual-info"))]) (casual-isearch . [(20240808 1638) ((emacs (29 1)) (casual-lib (1 1 0))) "Transient UI for I-Search" tar ((:commit . "812355ca972c81d9a31611572c490a2ea8ed8881") (:authors ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainers ("Charles Choi" . "kickingvegas@gmail.com")) (:maintainer "Charles Choi" . "kickingvegas@gmail.com") (:keywords "wp") (:url . "https://github.com/kickingvegas/casual-isearch"))]) @@ -499,7 +499,7 @@ (casual-re-builder . [(20240730 49) ((emacs (29 1)) (casual-lib (1 1 0))) "Transient UI for RE-Builder" tar ((:commit . "f52e0f2ae1faee0d2988fe701880f13fda66ab86") (: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-re-builder"))]) (casual-suite . [(20240730 426) ((emacs (29 1)) (casual-calc (1 9 0)) (casual-isearch (1 7 0)) (casual-dired (1 4 0)) (casual-ibuffer (1 0 1)) (casual-avy (1 2 0)) (casual-info (1 2 0)) (casual-re-builder (1 0 2)) (casual-bookmarks (1 0 0))) "A suite of opinionated Transient UIs" tar ((:commit . "328c9a02af56d56fe794fb073c0b720cb7ff7efc") (: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-suite"))]) (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 . [(20240808 20) ((emacs (25 1))) "Catppuccin for Emacs - 🍄 Soothing pastel theme for Emacs" tar ((:commit . "8affab8139ec90649e96c08e114ca66bea108045") (:maintainers ("Carsten Kragelund" . "carsten@kragelund.me")) (:maintainer "Carsten Kragelund" . "carsten@kragelund.me") (:url . "https://github.com/catppuccin/emacs"))]) + (catppuccin-theme . [(20240813 830) ((emacs (25 1))) "Catppuccin for Emacs - 🍄 Soothing pastel theme for Emacs" tar ((:commit . "177ecee1e5b8bbd05053cba5c93ebfae3b8fee8c") (:maintainers ("Jeremy Baxter" . "jeremy@baxters.nz")) (:maintainer "Jeremy Baxter" . "jeremy@baxters.nz") (:url . "https://github.com/catppuccin/emacs"))]) (cats . [(20230407 1316) ((emacs (26 1))) "Monads for Elisp" tar ((:commit . "7fc70db0eeb2c33ffba5c13c4cdc0f31c7b95537") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainers ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:url . "https://github.com/Fuco1/emacs-cats"))]) (cbm . [(20171116 1240) ((cl-lib (0 5))) "Switch to similar buffers." tar ((:commit . "5b41c936ba9f6d170309a85ffebc9939c1050b31") (:authors ("Lukas Fürmetz" . "fuermetz@mailbox.org")) (:maintainers ("Lukas Fürmetz" . "fuermetz@mailbox.org")) (:maintainer "Lukas Fürmetz" . "fuermetz@mailbox.org") (:keywords "buffers") (:url . "http://github.com/akermu/cbm.el"))]) (cbor . [(20230810 1653) ((emacs (25 1))) "CBOR utilities" tar ((:commit . "ba624ad3f8b726bee1d8dcb0a2a9e2b658bb4c9b") (:authors ("Oscar Najera" . "https://oscarnajera.com")) (:maintainers ("Oscar Najera" . "hi@oscarnajera.com")) (:maintainer "Oscar Najera" . "hi@oscarnajera.com") (:url . "https://github.com/Titan-C/cardano.el"))]) @@ -513,7 +513,7 @@ (cedit . [(20200816 526) nil "paredit-like commands for c-like languages" tar ((:commit . "cb38316903e6cfa8b8c978defa7e1dafcd4e0c12") (:url . "http://zk-phi.gitub.io/"))]) (celery . [(20170225 924) ((emacs (24)) (dash-functional (2 11 0)) (s (1 9 0)) (deferred (0 3 2))) "a minor mode to draw stats from celery and more?" tar ((:commit . "b3378dd81e5a717432123fb13d70201da5dc841a") (:authors ("ardumont" . "eniotna.t@gmail.com")) (:maintainers ("ardumont" . "eniotna.t@gmail.com")) (:maintainer "ardumont" . "eniotna.t@gmail.com") (:keywords "celery" "convenience") (:url . "https://github.com/ardumont/emacs-celery"))]) (celestial-mode-line . [(20230323 737) ((emacs (24))) "Show lunar phase and sunrise/-set time in modeline" tar ((:commit . "90056322d6664e2e2b593912e4d5e68f1468cafc") (:authors ("Peter" . "craven@gmx.net")) (:maintainers ("Peter" . "craven@gmx.net")) (:maintainer "Peter" . "craven@gmx.net") (:keywords "extensions") (:url . "https://github.com/ecraven/celestial-mode-line"))]) - (centaur-tabs . [(20240806 636) ((emacs (27 1)) (powerline (2 4))) "Aesthetic, modern looking customizable tabs plugin" tar ((:commit . "063534bd003850816ccd620e105619948785977b") (:authors ("Emmanuel Bustos" . "ema2159@gmail.com")) (:maintainers ("Jen-Chieh Shen" . "jcs090218@gmail.com")) (:maintainer "Jen-Chieh Shen" . "jcs090218@gmail.com") (:keywords "frames") (:url . "https://github.com/ema2159/centaur-tabs"))]) + (centaur-tabs . [(20240813 1143) ((emacs (27 1)) (powerline (2 4))) "Aesthetic, modern looking customizable tabs plugin" tar ((:commit . "ecee903518f1650421891f6c7bf521f200e22765") (:authors ("Emmanuel Bustos" . "ema2159@gmail.com")) (:maintainers ("Jen-Chieh Shen" . "jcs090218@gmail.com")) (:maintainer "Jen-Chieh Shen" . "jcs090218@gmail.com") (:keywords "frames") (:url . "https://github.com/ema2159/centaur-tabs"))]) (centered-cursor-mode . [(20230914 1358) nil "cursor stays vertically centered" tar ((:commit . "67ef719e685407dbc455c7430765e4e685fd95a9") (:authors ("André Riemann" . "andre.riemann@web.de")) (:maintainers ("André Riemann" . "andre.riemann@web.de")) (:maintainer "André Riemann" . "andre.riemann@web.de") (:keywords "convenience") (:url . "https://github.com/andre-r/centered-cursor-mode.el"))]) (centered-window . [(20220125 804) ((emacs (24 4))) "Center the text when there's only one window" tar ((:commit . "80965f6c6afe8d918481433984b493de72af5399") (:authors ("Anler Hernández Peral" . "inbox+emacs@anler.me")) (:maintainers ("Anler Hernández Peral" . "inbox+emacs@anler.me")) (:maintainer "Anler Hernández Peral" . "inbox+emacs@anler.me") (:keywords "faces" "windows") (:url . "https://github.com/anler/centered-window-mode"))]) (centimacro . [(20201225 1132) nil "Assign multiple macros as global key bindings" tar ((:commit . "0149877584b333c4f1953f0767f0cae23881b0df") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainers ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "macros") (:url . "https://github.com/abo-abo/centimacro"))]) @@ -524,7 +524,7 @@ (cfengine-code-style . [(20171111 1027) nil "C code style for CFEngine project." tar ((:commit . "92a25872a6d1de00c5bfc2b9455ccb0082bf6569") (:authors ("Mikhail Gusarov" . "mikhail.gusarov@cfengine.com")) (:maintainers ("Mikhail Gusarov" . "mikhail.gusarov@cfengine.com")) (:maintainer "Mikhail Gusarov" . "mikhail.gusarov@cfengine.com") (:url . "https://github.com/cfengine/core"))]) (cff . [(20160118 2018) ((cl-lib (0 5)) (emacs (24))) "Search of the C/C++ file header by the source and vice versa" tar ((:commit . "b6ab2a28e64ef06f281ec74cfe3114e450644dfa") (:authors ("Alexey Veretennikov" . "alexey.veretennikov@gmail.com")) (:maintainers ("Alexey Veretennikov" . "alexey.veretennikov@gmail.com")) (:maintainer "Alexey Veretennikov" . "alexey.veretennikov@gmail.com") (:keywords "find-file") (:url . "https://github.com/fourier/cff"))]) (cfml-mode . [(20190617 1130) ((emacs (25))) "Emacs mode for editing CFML files" tar ((:commit . "b06d7cee2af0ed5d55a94f0db80fc1f429a1829a") (:authors ("Andrew Myers" . "am2605@gmail.com")) (:maintainers ("Andrew Myers" . "am2605@gmail.com")) (:maintainer "Andrew Myers" . "am2605@gmail.com") (:url . "https://github.com/am2605/cfml-mode"))]) - (cfn-mode . [(20240721 805) ((emacs (27 0)) (f (0 20 0)) (s (1 12 0)) (yaml-mode (0 0 13))) "AWS cloudformation mode" tar ((:commit . "f492c795f1a0f4c6266bd6960224aedcf1ddbb0b") (:authors ("William Orr" . "will@worrbase.com")) (:maintainers ("William Orr" . "will@worrbase.com")) (:maintainer "William Orr" . "will@worrbase.com") (:keywords "convenience" "languages" "tools") (:url . "https://gitlab.com/worr/cfn-mode"))]) + (cfn-mode . [(20240811 805) ((emacs (27 0)) (f (0 20 0)) (s (1 12 0)) (yaml-mode (0 0 13))) "AWS cloudformation mode" tar ((:commit . "4784f043b214aa38c0c2c7f01e67802832a6c380") (:authors ("William Orr" . "will@worrbase.com")) (:maintainers ("William Orr" . "will@worrbase.com")) (:maintainer "William Orr" . "will@worrbase.com") (:keywords "convenience" "languages" "tools") (:url . "https://gitlab.com/worr/cfn-mode"))]) (cframe . [(20240223 2335) ((emacs (26)) (buffer-manage (0 11)) (dash (2 17 0))) "Customize a frame and fast switch size and positions" tar ((:commit . "580a20573ef413c269c032221de04abc1c97a6a8") (:keywords "frames") (:url . "https://github.com/plandes/cframe"))]) (cfrs . [(20220129 1149) ((emacs (26 1)) (dash (2 11 0)) (s (1 10 0)) (posframe (0 6 0))) "Child-frame based read-string" tar ((:commit . "f3a21f237b2a54e6b9f8a420a9da42b4f0a63121") (:authors ("Alexander Miller" . "alexanderm@web.de")) (:maintainers ("Alexander Miller" . "alexanderm@web.de")) (:maintainer "Alexander Miller" . "alexanderm@web.de") (:url . "https://github.com/Alexander-Miller/cfrs"))]) (cg . [(20240808 1902) ((emacs (26 1))) "Major mode for editing Constraint Grammar files" tar ((:commit . "07e099991185330b1d14e940e6e0e1d203972183") (:authors ("Kevin Brubeck Unhammer" . "unhammer@fsfe.org")) (:maintainers ("Kevin Brubeck Unhammer" . "unhammer@fsfe.org")) (:maintainer "Kevin Brubeck Unhammer" . "unhammer@fsfe.org") (:keywords "languages") (:url . "https://visl.sdu.dk/constraint_grammar.html"))]) @@ -535,7 +535,7 @@ (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 . [(20240807 1213) ((emacs (27 1)) (shell-maker (0 50 5))) "ChatGPT shell + buffer insert commands" tar ((:commit . "96f40272f64384915afe01274f8ae595d3ddb216") (:url . "https://github.com/xenodium/chatgpt-shell"))]) + (chatgpt-shell . [(20240814 938) ((emacs (27 1)) (shell-maker (0 50 5))) "ChatGPT shell + buffer insert commands" tar ((:commit . "6fb85746bd0d316c97e375dcc14eb5314778e74d") (:url . "https://github.com/xenodium/chatgpt-shell"))]) (chatu . [(20240518 615) ((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 . "f813f0bc926346fbd8151d2ae7079119d4657abb") (: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"))]) @@ -567,7 +567,7 @@ (chruby . [(20180114 1652) ((cl-lib (0 5))) "Emacs integration for chruby" tar ((:commit . "42bc6d521f832eca8e2ba210f30d03ad5529788f") (:authors ("Arne Brasseur" . "arne@arnebrasseur.net")) (:maintainers ("Arne Brasseur" . "arne@arnebrasseur.net")) (:maintainer "Arne Brasseur" . "arne@arnebrasseur.net") (:keywords "languages") (:url . "https://github.com/plexus/chruby.el"))]) (chyla-dark-theme . [(20240708 2033) ((emacs (24 1))) "Chyla.org - dark green color theme" tar ((:commit . "8d5c9a2eaaf04e0f1ad953a34b15e9777407b760") (:authors ("Adam Chyła https://chyla.org/" . "adam@chyla.org")) (:maintainers ("Adam Chyła https://chyla.org/" . "adam@chyla.org")) (:maintainer "Adam Chyła https://chyla.org/" . "adam@chyla.org") (:url . "https://github.com/chyla/ChylaDarkThemeForEmacs"))]) (chyla-theme . [(20240708 2017) ((emacs (24 1))) "Chyla.org - green color theme" tar ((:commit . "c2bb425eaff0975e0c7081f282d291f7853f8376") (:authors ("Adam Chyła https://chyla.org/" . "adam@chyla.org")) (:maintainers ("Adam Chyła https://chyla.org/" . "adam@chyla.org")) (:maintainer "Adam Chyła https://chyla.org/" . "adam@chyla.org") (:url . "https://github.com/chyla/ChylaThemeForEmacs"))]) - (cider . [(20240803 822) ((emacs (26)) (clojure-mode (5 19)) (parseedn (1 2 1)) (queue (0 2)) (spinner (1 7)) (seq (2 22)) (sesman (0 3 2)) (transient (0 4 1))) "Clojure Interactive Development Environment that Rocks" tar ((:commit . "5c1bac297341232d167a6d2beb79c39472597527") (:authors ("Tim King" . "kingtim@gmail.com") ("Phil Hagelberg" . "technomancy@gmail.com") ("Bozhidar Batsov" . "bozhidar@batsov.dev") ("Artur Malabarba" . "bruce.connor.am@gmail.com") ("Hugo Duncan" . "hugo@hugoduncan.org") ("Steve Purcell" . "steve@sanityinc.com")) (:maintainers ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "languages" "clojure" "cider") (:url . "https://www.github.com/clojure-emacs/cider"))]) + (cider . [(20240813 1832) ((emacs (26)) (clojure-mode (5 19)) (parseedn (1 2 1)) (queue (0 2)) (spinner (1 7)) (seq (2 22)) (sesman (0 3 2)) (transient (0 4 1))) "Clojure Interactive Development Environment that Rocks" tar ((:commit . "3c8af8b6787245e5dfd6dbb550a0ce8de1df4dcb") (:authors ("Tim King" . "kingtim@gmail.com") ("Phil Hagelberg" . "technomancy@gmail.com") ("Bozhidar Batsov" . "bozhidar@batsov.dev") ("Artur Malabarba" . "bruce.connor.am@gmail.com") ("Hugo Duncan" . "hugo@hugoduncan.org") ("Steve Purcell" . "steve@sanityinc.com")) (:maintainers ("Bozhidar Batsov" . "bozhidar@batsov.dev")) (:maintainer "Bozhidar Batsov" . "bozhidar@batsov.dev") (:keywords "languages" "clojure" "cider") (:url . "https://www.github.com/clojure-emacs/cider"))]) (cider-decompile . [(20151122 537) ((cider (0 3 0)) (javap-mode (9))) "decompilation extension for cider" tar ((:commit . "5d87035f3c3c14025e8f01c0c53d0ce2c8f56651") (:keywords "languages" "clojure" "cider") (:url . "http://www.github.com/clojure-emacs/cider-decompile"))]) (cider-eval-sexp-fu . [(20190311 2152) ((emacs (24)) (eval-sexp-fu (0 5 0))) "Briefly highlights an evaluated sexp." tar ((:commit . "7fd229f1441356866aedba611fd0cf4e89b50921") (:authors ("Sylvain Benner" . "sylvain.benner@gmail.com")) (:maintainers ("Sylvain Benner" . "sylvain.benner@gmail.com")) (:maintainer "Sylvain Benner" . "sylvain.benner@gmail.com") (:keywords "languages" "clojure" "cider"))]) (cider-hydra . [(20190816 1121) ((cider (0 22 0)) (hydra (0 13 0))) "Hydras for CIDER." tar ((:commit . "c3b8a15d72dddfbc390ab6a454bd7e4c765a2c95") (:authors ("Tianxiang Xiong" . "tianxiang.xiong@gmail.com")) (:maintainers ("Tianxiang Xiong" . "tianxiang.xiong@gmail.com")) (:maintainer "Tianxiang Xiong" . "tianxiang.xiong@gmail.com") (:keywords "convenience" "tools") (:url . "https://github.com/clojure-emacs/cider-hydra"))]) @@ -778,7 +778,7 @@ (connection . [(20191111 446) nil "TCP-based client connection" tar ((:commit . "c9cad101100975e88873636bfd426b7a19304ebd") (:authors ("Torsten Hilbrich" . "torsten.hilbrich@gmx.net")) (:maintainers ("Torsten Hilbrich" . "torsten.hilbrich@gmx.net")) (:maintainer "Torsten Hilbrich" . "torsten.hilbrich@gmx.net") (:keywords "network"))]) (conner . [(20240707 2220) ((emacs (29 1))) "Define and run project specific commands" tar ((:commit . "583d8288b07f4372b68e1c06917eb77c419c555d") (:keywords "tools") (:url . "https://github.com/tralph3/conner"))]) (constant-theme . [(20180921 1012) ((emacs (24 1))) "A calm, dark, almost monochrome color theme." tar ((:commit . "0feb9f99d708633d62fa548c953ebbe68fd70de0") (:authors ("Jannis Pohlmann" . "contact@jannispohlmann.de")) (:maintainers ("Jannis Pohlmann" . "contact@jannispohlmann.de")) (:maintainer "Jannis Pohlmann" . "contact@jannispohlmann.de") (:keywords "themes") (:url . "https://github.com/jannis/emacs-constant-theme"))]) - (consult . [(20240725 508) ((emacs (27 1)) (compat (30))) "Consulting completing-read" tar ((:commit . "4889458dccf842ab6223099f8a73ff8b147e9459") (:maintainers ("Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainer "Daniel Mendler" . "mail@daniel-mendler.de") (:keywords "matching" "files" "completion") (:url . "https://github.com/minad/consult"))]) + (consult . [(20240811 1858) ((emacs (27 1)) (compat (30))) "Consulting completing-read" tar ((:commit . "3d0fc6a8c6a74b21ca854b8632b3f58e0e513b85") (:maintainers ("Daniel Mendler" . "mail@daniel-mendler.de")) (:maintainer "Daniel Mendler" . "mail@daniel-mendler.de") (:keywords "matching" "files" "completion") (:url . "https://github.com/minad/consult"))]) (consult-ag . [(20230227 406) ((emacs (27 1)) (consult (0 32))) "The silver searcher integration using Consult" tar ((:commit . "9eb4df265aedf2628a714610c2ade6d2f21de053") (:authors ("Kanon Kakuno and contributors" . "yadex205@outlook.jp")) (:maintainers ("Kanon Kakuno and contributors" . "yadex205@outlook.jp")) (:maintainer "Kanon Kakuno and contributors" . "yadex205@outlook.jp") (:url . "https://github.com/yadex205/consult-ag"))]) (consult-codesearch . [(20230315 1424) ((emacs (27 1)) (consult (0 20))) "Consult interface for codesearch" tar ((:commit . "51df545bb57b468058245950322ae15f6c3a0ce2") (:authors ("Youngjoo Lee" . "youngker@gmail.com")) (:maintainers ("Youngjoo Lee" . "youngker@gmail.com")) (:maintainer "Youngjoo Lee" . "youngker@gmail.com") (:keywords "tools") (:url . "https://github.com/youngker/consult-codesearch"))]) (consult-company . [(20230606 1824) ((emacs (27 1)) (company (0 9)) (consult (0 9))) "Consult frontend for company" tar ((:commit . "6e309fa9115c9ecd29aa27bff4e3b733979e5dbc") (:authors ("mohsin kaleem" . "mohkale@kisara.moe")) (:maintainers ("mohsin kaleem" . "mohkale@kisara.moe")) (:maintainer "mohsin kaleem" . "mohkale@kisara.moe") (:url . "https://github.com/mohkale/consult-company"))]) @@ -877,7 +877,7 @@ (crystal-playground . [(20180830 501) ((emacs (25)) (crystal-mode (0 1 2))) "Local crystal playground for short code snippets." tar ((:commit . "532dc7e4239eb4bdd241bc4347d34760344c1ebb") (:keywords "tools" "crystal") (:url . "https://github.com/jasonrobot/crystal-playground"))]) (csgo-conf-mode . [(20161209 1619) nil "CS:GO Configuration files syntax highlighting" tar ((:commit . "df45ca833eb68c394dd03acce5733a33c3b06bf8") (: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-csgo-conf-mode"))]) (csharp-mode . [(20221126 2005) ((emacs (26 1))) "C# mode derived mode" tar ((:commit . "d8b058c9e9d0429ea7e81d121ce19b064bd7e0f5") (:authors ("Theodor Thornhill" . "theo@thornhill.no")) (:maintainers ("Jostein Kjønigsen" . "jostein@gmail.com") ("Theodor Thornhill" . "theo@thornhill.no")) (:maintainer "Jostein Kjønigsen" . "jostein@gmail.com") (:keywords "c#" "languages" "oop" "mode") (:url . "https://github.com/emacs-csharp/csharp-mode"))]) - (csound-mode . [(20240716 1423) ((emacs (25)) (shut-up (0 3 2)) (multi (2 0 1)) (dash (2 16 0)) (highlight (0))) "A major mode for interacting and coding Csound" tar ((:commit . "a83fabdccf1af72295318bf15cb69ee5ac5fb2c6") (:authors ("Hlöðver Sigurðsson" . "hlolli@gmail.com")) (:maintainers ("Hlöðver Sigurðsson" . "hlolli@gmail.com")) (:maintainer "Hlöðver Sigurðsson" . "hlolli@gmail.com") (:url . "https://github.com/hlolli/csound-mode"))]) + (csound-mode . [(20240813 1802) ((emacs (25)) (shut-up (0 3 2)) (multi (2 0 1)) (dash (2 16 0)) (highlight (0))) "A major mode for interacting and coding Csound" tar ((:commit . "bea3a94f4e1b4d3b32a4ed7943d2beda794908b5") (:authors ("Hlöðver Sigurðsson" . "hlolli@gmail.com")) (:maintainers ("Hlöðver Sigurðsson" . "hlolli@gmail.com")) (:maintainer "Hlöðver Sigurðsson" . "hlolli@gmail.com") (:url . "https://github.com/hlolli/csound-mode"))]) (csproj-mode . [(20200801 1732) ((emacs (24))) "Work with .NET project files (csproj, vbproj)" tar ((:commit . "a7f0f4610c976a28c41b9b8299892f88b5d0336c") (:authors ("Omair Majid" . "omair.majid@gmail.com")) (:maintainers ("Omair Majid" . "omair.majid@gmail.com")) (:maintainer "Omair Majid" . "omair.majid@gmail.com") (:keywords "languages" "tools") (:url . "https://github.com/omajid/csproj-mode"))]) (css-autoprefixer . [(20180311 1600) ((emacs (24))) "Adds autoprefix to CSS" tar ((:commit . "386a5defc8543a3b87820f1761c075c7d1d93b38") (:authors ("Kyung Mo Kweon and contributors" . "kkweon@gmail.com")) (:maintainers ("Kyung Mo Kweon and contributors" . "kkweon@gmail.com")) (:maintainer "Kyung Mo Kweon and contributors" . "kkweon@gmail.com") (:keywords "convenience" "usability" "css") (:url . "https://github.com/kkweon/emacs-css-autoprefixer"))]) (css-comb . [(20160416 559) nil "Sort CSS properties in a particular order using CSS Comb" tar ((:commit . "6fa45e5af8a8bd3af6c1154cde3540e32c4206ee") (:authors ("Charanjit Singh" . "ckhabra@gmail.com")) (:maintainers ("Charanjit Singh" . "ckhabra@gmail.com")) (:maintainer "Charanjit Singh" . "ckhabra@gmail.com") (:url . "https://github.com/channikhabra/css-comb.el"))]) @@ -917,14 +917,14 @@ (cypher-mode . [(20151110 1142) nil "major mode for editing cypher scripts" tar ((:commit . "ce8543d7877c736c574a17b49874c9dcdc7a06d6") (:authors ("François-Xavier Bois" . "fxboisATGoogleMailService")) (:keywords "cypher" "graph") (:url . "http://github.com/fxbois/cypher-mode"))]) (cython-mode . [(20221130 1257) nil "Major mode for editing Cython files" tar ((:commit . "3e4790559d3168fe992cf2aa62f01423038cedb5"))]) (czech-holidays . [(20160113 1752) nil "Adds a list of Czech public holidays to Emacs calendar" tar ((:commit . "d19828122cf3322bcf50601cefa4ac385d2d8f82") (:authors ("David Chkhikvadze" . "david.chk@outlook.com")) (:maintainers ("David Chkhikvadze" . "david.chk@outlook.com")) (:maintainer "David Chkhikvadze" . "david.chk@outlook.com") (:keywords "calendar"))]) - (d-mode . [(20240722 232) ((emacs (25 1))) "D Programming Language major mode for (X)Emacs" tar ((:commit . "def180655199b1143207545f63bd188852082b2b") (:maintainers ("Russel Winder" . "russel@winder.org.uk") ("Vladimir Panteleev" . "vladimir@thecybershadow.net")) (:maintainer "Russel Winder" . "russel@winder.org.uk") (:keywords "d" "programming" "language" "emacs" "cc-mode"))]) + (d-mode . [(20240813 659) ((emacs (25 1))) "D Programming Language major mode for (X)Emacs" tar ((:commit . "9b1676d70edbc2f2788130adfd5797515a5c8538") (:maintainers ("Russel Winder" . "russel@winder.org.uk") ("Vladimir Panteleev" . "vladimir@thecybershadow.net")) (:maintainer "Russel Winder" . "russel@winder.org.uk") (:keywords "d" "programming" "language" "emacs" "cc-mode"))]) (d2-mode . [(20240707 1850) ((emacs (26 1))) "Major mode for working with d2 graphs" tar ((:commit . "69374e0249df20139f3f2d475de9eae2b201d019") (:authors ("Andor Kesselman" . "andor@henosisknot.com")) (:maintainers ("Andor Kesselman" . "andor@henosisknot.com")) (:maintainer "Andor Kesselman" . "andor@henosisknot.com") (:keywords "d2" "graphs" "tools" "processes") (:url . "https://github.com/andorsk/d2-mode"))]) (dactyl-mode . [(20140906 1725) nil "Major mode for editing Pentadactyl config files" tar ((:commit . "cc55fe6b987271d9647492b8df4c812d884f661f") (:keywords "languages" "vim") (:url . "https://github.com/luxbock/dactyl-mode"))]) (dad-joke . [(20170928 658) ((emacs (24))) "Get/display dad jokes" tar ((:commit . "bee47e7b746b403228fa7d7361cb095de19ac9ba") (:authors ("Dave Pearson" . "davep@davep.org")) (:maintainers ("Dave Pearson" . "davep@davep.org")) (:maintainer "Dave Pearson" . "davep@davep.org") (:keywords "games") (:url . "https://github.com/davep/dad-joke.el"))]) (daemons . [(20231212 1324) ((emacs (25 1)) (s (1 13 0)) (compat (29 1 4 2))) "UI for managing init system daemons (services)" tar ((:commit . "6b6b97b7bac3040cfc58ea5ca7bd9dc9003068fb") (:keywords "unix" "convenience") (:url . "https://github.com/cbowdon/daemons.el"))]) (dakrone-light-theme . [(20170808 2140) nil "dakrone's custom light theme" tar ((:commit . "06f198dc8b4ca7421990b30a23d89c8e0b8c5de4") (:authors ("Lee Hinman" . "lee_AT_writequit.org")) (:maintainers ("Lee Hinman" . "lee_AT_writequit.org")) (:maintainer "Lee Hinman" . "lee_AT_writequit.org") (:keywords "color" "themes" "faces") (:url . "https://github.com/dakrone/dakrone-light-theme"))]) (dakrone-theme . [(20170801 1933) nil "dakrone's custom dark theme" tar ((:commit . "232ad1be5f3572dcbdf528f1655109aa355a6937") (:authors ("Lee Hinman" . "lee_AT_writequit.org")) (:maintainers ("Lee Hinman" . "lee_AT_writequit.org")) (:maintainer "Lee Hinman" . "lee_AT_writequit.org") (:keywords "color" "themes") (:url . "https://github.com/dakrone/dakrone-theme"))]) - (dall-e-shell . [(20240806 1414) ((emacs (27 1)) (shell-maker (0 49 1))) "Interaction mode for DALL-E" tar ((:commit . "e42e4d42b8978cdf8d7e7e8a5b7936cba80a035a") (:url . "https://github.com/xenodium/chatgpt-shell"))]) + (dall-e-shell . [(20240814 938) ((emacs (27 1)) (shell-maker (0 49 1))) "Interaction mode for DALL-E" tar ((:commit . "6fb85746bd0d316c97e375dcc14eb5314778e74d") (:url . "https://github.com/xenodium/chatgpt-shell"))]) (daml-lsp . [(20231101 1818) ((daml-mode (1 0)) (dash (2 18 0)) (f (0 20 0)) (ht (2 3)) (lsp-mode (7 0))) "LSP client definition for daml" tar ((:commit . "26ea6a1b34c49aaa5a2b395a0468c8af710bfab7") (:url . "https://github.com/bartfaitamas/daml-mode"))]) (daml-mode . [(20231106 916) ((emacs (27 1)) (haskell-mode (16 1))) "Major mode for daml" tar ((:commit . "3ba1166edd4c22402996625b1f8a05a2d5b1cbc6") (:url . "https://github.com/bartfaitamas/daml-mode"))]) (danneskjold-theme . [(20240723 1000) nil "Beautiful high-contrast Emacs theme." tar ((:commit . "d495ba64e4a9e3e44b028b9fbc3898da3348ffdc") (:authors ("Dmitry Akatov" . "akatovda@yandex.com")) (:maintainer "Dmitry Akatov" . "akatovda@yandex.com") (:url . "https://github.com/rails-to-cosmos/danneskjold-theme"))]) @@ -946,7 +946,7 @@ (dash-at-point . [(20211023 104) nil "Search the word at point with Dash" tar ((:commit . "fba1a6f42ea51d05110e12c62bdced664059eb55") (:authors ("Shinji Tanaka" . "shinji.tanaka@gmail.com")) (:maintainers ("Shinji Tanaka" . "shinji.tanaka@gmail.com")) (:maintainer "Shinji Tanaka" . "shinji.tanaka@gmail.com") (:url . "https://github.com/stanaka/dash-at-point"))]) (dash-docs . [(20210830 926) ((emacs (24 4)) (cl-lib (0 5)) (async (1 9 3))) "Offline documentation browser using Dash docsets." tar ((:commit . "29848b6b347ac520f7646c200ed2ec36cea3feda") (:authors ("Raimon Grau" . "raimonster@gmail.com") ("Toni Reina" . "areina0@gmail.com") ("Bryan Gilbert" . "bryan@bryan.sh")) (:maintainers ("Raimon Grau" . "raimonster@gmail.com") ("Toni Reina" . "areina0@gmail.com") ("Bryan Gilbert" . "bryan@bryan.sh")) (:maintainer "Raimon Grau" . "raimonster@gmail.com") (:keywords "docs") (:url . "http://github.com/areina/helm-dash"))]) (dash-functional . [(20210826 1149) ((dash (2 18 0))) "Collection of useful combinators for Emacs Lisp" tar ((:commit . "39d067b9fbb2db65fc7a6938bfb21489ad990cb4") (:authors ("Matus Goljer" . "matus.goljer@gmail.com") ("Magnar Sveen" . "magnars@gmail.com")) (:maintainers ("Matus Goljer" . "matus.goljer@gmail.com") ("Magnar Sveen" . "magnars@gmail.com")) (:maintainer "Matus Goljer" . "matus.goljer@gmail.com") (:keywords "extensions" "lisp") (:url . "https://github.com/magnars/dash.el"))]) - (dashboard . [(20240529 2058) ((emacs (26 1))) "A startup screen extracted from Spacemacs" tar ((:commit . "3852301f9c6f3104d9cc98389612b5ef3452a7de") (:authors ("Rakan Al-Hneiti" . "rakan.alhneiti@gmail.com")) (:maintainers ("Jesús Martínez" . "jesusmartinez93@gmail.com") ("Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Jesús Martínez" . "jesusmartinez93@gmail.com") (:keywords "startup" "screen" "tools" "dashboard") (:url . "https://github.com/emacs-dashboard/emacs-dashboard"))]) + (dashboard . [(20240813 838) ((emacs (26 1))) "A startup screen extracted from Spacemacs" tar ((:commit . "187699e2d80f6a3b0ec7b2fb2e1d7ece2712208f") (:authors ("Rakan Al-Hneiti" . "rakan.alhneiti@gmail.com")) (:maintainers ("Jesús Martínez" . "jesusmartinez93@gmail.com") ("Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Jesús Martínez" . "jesusmartinez93@gmail.com") (:keywords "startup" "screen" "tools" "dashboard") (:url . "https://github.com/emacs-dashboard/emacs-dashboard"))]) (dashboard-hackernews . [(20220516 1809) ((emacs (24)) (dashboard (1 2 5)) (request (0 3 0))) "Display Hacker News on dashboard" tar ((:commit . "34d86bffcde7e6d10ffa7c5080a71a144f01f3aa") (:authors ("Hayato KAJIYAMA" . "kaji1216@gmail.com")) (:maintainers ("Hayato KAJIYAMA" . "kaji1216@gmail.com")) (:maintainer "Hayato KAJIYAMA" . "kaji1216@gmail.com") (:url . "https://github.com/hyakt/emacs-dashboard-hackernews"))]) (dashboard-ls . [(20240101 841) ((emacs (26 1)) (dashboard (1 2 5))) "Display files/directories in current directory on Dashboard" tar ((:commit . "bc79640e8fcc625ff1af31d5e17b054b1f535f39") (:authors ("Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Jen-Chieh" . "jcs090218@gmail.com") (:keywords "convenience" "directory" "file" "show") (:url . "https://github.com/emacs-dashboard/dashboard-ls"))]) (dashboard-project-status . [(20190202 1354) ((emacs (24)) (git (0 1 1)) (dashboard (1 2 5))) "Display a git project status in a dashboard widget." tar ((:commit . "7675c138e9df8fe2c626e7ba9bbb8b6717671a41") (:authors ("Jason Duncan" . "jasond496@msn.com")) (:maintainers ("Jason Duncan" . "jasond496@msn.com")) (:maintainer "Jason Duncan" . "jasond496@msn.com") (:url . "https://github.com/functionreturnfunction/dashboard-project-status"))]) @@ -1138,7 +1138,7 @@ (dokuwiki-mode . [(20170223 1301) nil "Major mode for DokuWiki document" tar ((:commit . "e4e116f6fcc373e3f5937c1a7daa5c2c9c6d3fa1") (:authors ("Tsunenobu Kai" . "kai2nenobu@gmail.com")) (:maintainers ("Tsunenobu Kai" . "kai2nenobu@gmail.com")) (:maintainer "Tsunenobu Kai" . "kai2nenobu@gmail.com") (:keywords "hypermedia" "text" "dokuwiki") (:url . "https://github.com/kai2nenobu/emacs-dokuwiki-mode"))]) (dollaro . [(20151123 1302) ((s (1 6 0))) "simple text templates" tar ((:commit . "500127f0172ac7a1eec627e026b59136580a74ac") (:authors ("Alessandro Piras" . "laynor@gmail.com")) (:maintainers ("Alessandro Piras" . "laynor@gmail.com")) (:maintainer "Alessandro Piras" . "laynor@gmail.com") (:keywords "tools" "convenience"))]) (doom . [(20180301 2308) ((cl-lib (0 5))) "DOM implementation and manipulation library" tar ((:commit . "e59040aefc92dd9b3134eb623624307fb9e4327b") (:authors ("Alex Schroeder" . "alex@gnu.org") ("Henrik.Motakef" . "elisp@henrik-motakef.de") ("Katherine Whitlock" . "toroidal-code@gmail.com") ("Syohei YOSHIDA" . "syohex@gmail.com")) (:keywords "xml" "dom") (:url . "http://www.github.com/kensanata/doom.el/"))]) - (doom-modeline . [(20240810 1702) ((emacs (25 1)) (compat (29 1 4 5)) (nerd-icons (0 1 0)) (shrink-path (0 3 1))) "A minimal and modern mode-line" tar ((:commit . "35215cbe0293e9404ab2ce96177a995f9623832a") (:authors ("Vincent Zhang" . "seagle0128@gmail.com")) (:maintainers ("Vincent Zhang" . "seagle0128@gmail.com")) (:maintainer "Vincent Zhang" . "seagle0128@gmail.com") (:keywords "faces" "mode-line") (:url . "https://github.com/seagle0128/doom-modeline"))]) + (doom-modeline . [(20240811 1437) ((emacs (25 1)) (compat (29 1 4 5)) (nerd-icons (0 1 0)) (shrink-path (0 3 1))) "A minimal and modern mode-line" tar ((:commit . "790e6817814a1fa893a91722861cba9424b0f004") (:authors ("Vincent Zhang" . "seagle0128@gmail.com")) (:maintainers ("Vincent Zhang" . "seagle0128@gmail.com")) (:maintainer "Vincent Zhang" . "seagle0128@gmail.com") (:keywords "faces" "mode-line") (:url . "https://github.com/seagle0128/doom-modeline"))]) (doom-modeline-now-playing . [(20240522 1704) ((emacs (24 4)) (doom-modeline (3 0 0)) (async (1 9 3))) "Segment for Doom Modeline to show playerctl information" tar ((:commit . "1532f324f98a234aa14e12ebdfd17cebba978d6a") (:authors ("Ellis Kenyő" . "me@elken.dev")) (:maintainers ("Ellis Kenyő" . "me@elken.dev")) (:maintainer "Ellis Kenyő" . "me@elken.dev") (:url . "https://github.com/elken/doom-modeline-now-playing"))]) (doom-themes . [(20240809 2135) ((emacs (25 1)) (cl-lib (0 5))) "an opinionated pack of modern color-themes" tar ((:commit . "c589b245d643dcc5ec93054ea436efc5255f9b88") (:authors ("Henrik Lissner" . "contact@henrik.io")) (:maintainers ("Henrik Lissner" . "contact@henrik.io")) (:maintainer "Henrik Lissner" . "contact@henrik.io") (:keywords "themes" "faces") (:url . "https://github.com/doomemacs/themes"))]) (dot-env . [(20230820 2014) ((emacs (24 4)) (s (1 13 0))) "Dotenv functionality" tar ((:commit . "83ce690e8ef9175fc621c85d5fbef4f7ace7b7a8") (:keywords "convenience" "dotenv" "environment" "configuration") (:url . "https://github.com/amodelbello/dot-env.el"))]) @@ -1212,7 +1212,7 @@ (easy-kill . [(20220511 557) ((emacs (25)) (cl-lib (0 5))) "kill & mark things easily" tar ((:commit . "de7d66c3c864a4722a973ee9bc228a14be49ba0c") (:authors ("Leo Liu" . "sdl.web@gmail.com")) (:maintainers ("Leo Liu" . "sdl.web@gmail.com")) (:maintainer "Leo Liu" . "sdl.web@gmail.com") (:keywords "killing" "convenience") (:url . "https://github.com/leoliu/easy-kill"))]) (easy-kill-extras . [(20240122 1649) ((easy-kill (0 9 4))) "Extra functions for easy-kill." tar ((:commit . "6ec0a1ff47aee681f7aa7af4250ede75815385f2") (:authors ("Akinori MUSHA" . "knu@iDaemons.org")) (:maintainers ("Akinori MUSHA" . "knu@iDaemons.org")) (:maintainer "Akinori MUSHA" . "knu@iDaemons.org") (:keywords "killing" "convenience") (:url . "https://github.com/knu/easy-kill-extras.el"))]) (easy-repeat . [(20150516 848) ((emacs (24 4))) "Repeat easily" tar ((:commit . "060f0e6801c82c40c06961dc0528a00e18947a8c") (:authors ("Chunyang Xu" . "xuchunyang56@gmail.com")) (:maintainers ("Chunyang Xu" . "xuchunyang56@gmail.com")) (:maintainer "Chunyang Xu" . "xuchunyang56@gmail.com") (:keywords "repeat" "convenience") (:url . "https://github.com/xuchunyang/easy-repeat.el"))]) - (easysession . [(20240809 2029) ((emacs (25 1)) (f (0 18 2))) "Easily persist and restore your editing sessions" tar ((:commit . "3558bcf052e5cd9936ffd2a0d648d991a35581bd") (:keywords "convenience") (:url . "https://github.com/jamescherti/easysession.el"))]) + (easysession . [(20240809 2239) ((emacs (25 1)) (f (0 18 2))) "Easily persist and restore your editing sessions" tar ((:commit . "c7d5b38fa82a54a6710fed01f00756764d78dd7a") (:keywords "convenience") (:url . "https://github.com/jamescherti/easysession.el"))]) (ebdb-mua-sidecar . [(20240428 1852) ((emacs (28 1)) (universal-sidecar (1 5 1)) (ebdb (0 8 20))) "EBDB Integration for Universal Sidecar" tar ((:commit . "4c78015d10caba9c700e6e6b582004ae1c1d5344") (:authors ("Samuel W. Flint" . "me@samuelwflint.com")) (:maintainers ("Samuel W. Flint" . "me@samuelwflint.com")) (:maintainer "Samuel W. Flint" . "me@samuelwflint.com") (:keywords "mail" "convenience") (:url . "https://git.sr.ht/~swflint/emacs-universal-sidecar"))]) (ebf . [(20210225 1211) ((dash (2 18 0)) (cl-lib (0 5))) "brainfuck language transpiler to Emacs Lisp" tar ((:commit . "6cbeb4d62416f4cfd5be8906667342af8ecc44a6") (:authors ("Alexey Kutepov" . "reximkut@gmail.com")) (:maintainers ("Alexey Kutepov" . "reximkut@gmail.com")) (:maintainer "Alexey Kutepov" . "reximkut@gmail.com") (:url . "http://github.com/rexim/ebf"))]) (ebib . [(20240723 1149) ((parsebib (4 0)) (emacs (27 1)) (compat (29 1 4 3))) "a BibTeX database manager" tar ((:commit . "5ea510a1e4ab17a442352d0212777da5f78d4be2") (:authors ("Joost Kremers" . "joostkremers@fastmail.fm")) (:maintainers ("Joost Kremers" . "joostkremers@fastmail.fm")) (:maintainer "Joost Kremers" . "joostkremers@fastmail.fm") (:keywords "text" "bibtex") (:url . "http://joostkremers.github.io/ebib/"))]) @@ -1240,7 +1240,7 @@ (edit-list . [(20100930 1443) nil "edit a single list" tar ((:commit . "f460d3f9e208a4e606fe6ded307f1b011916ca71") (:authors ("Michael Olson" . "mwolson@gnu.org")) (:maintainers ("Michael Olson" . "mwolson@gnu.org")) (:maintainer "Michael Olson" . "mwolson@gnu.org") (:url . "http://mwolson.org/static/dist/elisp/edit-list.el"))]) (edit-server . [(20220908 1014) nil "server that responds to edit requests from Chrome" tar ((:commit . "3ce09c6eb2919d56ef052b1584bba6abb12f7e99") (:authors ("Alex Bennée" . "alex@bennee.com")) (:maintainers ("Alex Bennée" . "alex@bennee.com")) (:maintainer "Alex Bennée" . "alex@bennee.com") (:url . "https://github.com/stsquad/emacs_chrome"))]) (edit-server-htmlize . [(20130329 2248) ((edit-server (1 9))) "(de)HTMLization hooks for edit-server.el" tar ((:commit . "e7f8dadfabe869c77ca241cd6fbd4c52bd908392") (:authors ("Roland McGrath" . "roland@hack.frob.com")) (:maintainers ("Roland McGrath" . "roland@hack.frob.com")) (:maintainer "Roland McGrath" . "roland@hack.frob.com") (:url . "https://github.com/frobtech/edit-server-htmlize"))]) - (editorconfig . [(20240728 1714) ((emacs (26 1))) "EditorConfig Emacs Plugin" tar ((:commit . "fbd078ad647888c971abe3508dd0ba27fac97cb9") (:authors ("EditorConfig Team" . "editorconfig@googlegroups.com")) (:maintainers ("EditorConfig Team" . "editorconfig@googlegroups.com")) (:maintainer "EditorConfig Team" . "editorconfig@googlegroups.com") (:keywords "convenience" "editorconfig") (:url . "https://github.com/editorconfig/editorconfig-emacs#readme"))]) + (editorconfig . [(20240813 801) ((emacs (26 1))) "EditorConfig Emacs Plugin" tar ((:commit . "648f0cf9aeb72db77b252832a58367332b7bc055") (:authors ("EditorConfig Team" . "editorconfig@googlegroups.com")) (:maintainers ("EditorConfig Team" . "editorconfig@googlegroups.com")) (:maintainer "EditorConfig Team" . "editorconfig@googlegroups.com") (:keywords "convenience" "editorconfig") (:url . "https://github.com/editorconfig/editorconfig-emacs#readme"))]) (editorconfig-charset-extras . [(20180223 457) ((editorconfig (0 6 0))) "Extra EditorConfig Charset Support" tar ((:commit . "ddf60923c6f4841cb593b2ea04c9c710a01d262f") (:authors ("10sr" . "8.slashes@gmail.com")) (:maintainers ("10sr" . "8.slashes@gmail.com")) (:maintainer "10sr" . "8.slashes@gmail.com") (:keywords "tools") (:url . "https://github.com/10sr/editorconfig-charset-extras-el"))]) (editorconfig-custom-majormode . [(20180816 244) ((editorconfig (0 6 0))) "Decide major-mode and mmm-mode from EditorConfig" tar ((:commit . "13ad1c83f847bedd4b3a19f9df7fd925853b19de") (:authors ("10sr" . "8slashes+el[at]gmail[dot]com")) (:maintainers ("10sr" . "8slashes+el[at]gmail[dot]com")) (:maintainer "10sr" . "8slashes+el[at]gmail[dot]com") (:keywords "editorconfig" "util") (:url . "https://github.com/10sr/editorconfig-custom-majormode-el"))]) (editorconfig-domain-specific . [(20180505 924) ((cl-lib (0 5)) (editorconfig (0 6 0))) "Apply brace style and other \"domain-specific\" EditorConfig properties" tar ((:commit . "e9824160fb2e466afa755240ee3ab7cc5657fb04") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainers ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "editorconfig" "util") (:url . "https://github.com/lassik/editorconfig-emacs-domain-specific"))]) @@ -1270,7 +1270,7 @@ (eink-theme . [(20190219 858) nil "E Ink color theme" tar ((:commit . "326b07523dcb076d6209cdbc7fdbb73df296dbdb") (:authors ("Marian Schubert" . "marian.schubert@gmail.com")) (:maintainers ("Marian Schubert" . "marian.schubert@gmail.com")) (:maintainer "Marian Schubert" . "marian.schubert@gmail.com") (:url . "http://github.com/maio/eink-emacs"))]) (ejc-sql . [(20240106 1848) ((emacs (26 3)) (clomacs (0 0 5)) (dash (2 16 0)) (spinner (1 7 3))) "Emacs SQL client uses Clojure JDBC." tar ((:commit . "b80b773238719fa7160e598219f300dfbc4db06d") (:authors ("Kostafey" . "kostafey@gmail.com")) (:maintainers ("Kostafey" . "kostafey@gmail.com")) (:maintainer "Kostafey" . "kostafey@gmail.com") (:keywords "sql" "jdbc") (:url . "https://github.com/kostafey/ejc-sql"))]) (ejson-mode . [(20190720 2138) ((emacs (25))) "Major mode for editing ejson files." tar ((:commit . "9630dfac9549779711dbe89e621f516bb4b3a354") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/dantecatalfamo/ejson-mode"))]) - (ekg . [(20240728 1623) ((triples (0 3 5)) (emacs (28 1)) (llm (0 17 0))) "A system for recording and linking information" tar ((:commit . "a03117f117e2eaf63c56061e2beb7f4868111596") (:authors ("Andrew Hyatt" . "ahyatt@gmail.com")) (:maintainers ("Andrew Hyatt" . "ahyatt@gmail.com")) (:maintainer "Andrew Hyatt" . "ahyatt@gmail.com") (:keywords "outlines" "hypermedia") (:url . "https://github.com/ahyatt/ekg"))]) + (ekg . [(20240812 352) ((triples (0 3 5)) (emacs (28 1)) (llm (0 17 0))) "A system for recording and linking information" tar ((:commit . "00ae22d95ee7a006214589b905fc9b7e268141b2") (:authors ("Andrew Hyatt" . "ahyatt@gmail.com")) (:maintainers ("Andrew Hyatt" . "ahyatt@gmail.com")) (:maintainer "Andrew Hyatt" . "ahyatt@gmail.com") (:keywords "outlines" "hypermedia") (:url . "https://github.com/ahyatt/ekg"))]) (el-autoyas . [(20120918 1317) nil "Automatically create Emacs-Lisp Yasnippets" tar ((:commit . "bde0251ecb504f585dfa27c205c8e312655310cc") (:keywords "emacs" "lisp" "mode" "yasnippet") (:url . "https://github.com/mlf176f2/el-autoyas.el"))]) (el-fetch . [(20230624 2) ((emacs (25 1))) "Show system information in Neofetch-like style (eg CPU, RAM)" tar ((:commit . "7907fd7829ca55b21a62d23c17066fdfde9cd07c") (:authors ("Maciej Barć" . "xgqt@riseup.net")) (:maintainers ("Maciej Barć" . "xgqt@riseup.net")) (:maintainer "Maciej Barć" . "xgqt@riseup.net") (:keywords "games") (:url . "https://gitlab.com/xgqt/emacs-el-fetch"))]) (el-fly-indent-mode . [(20180422 243) ((emacs (25))) "Indent Emacs Lisp on the fly" tar ((:commit . "1dd4b907ff4d9581c18b4e38e8719e83ba0dace1") (:authors ("Jiahao Li" . "jiahaowork@gmail.com")) (:maintainers ("Jiahao Li" . "jiahaowork@gmail.com")) (:maintainer "Jiahao Li" . "jiahaowork@gmail.com") (:keywords "lisp" "languages") (:url . "https://github.com/jiahaowork/el-fly-indent-mode.el"))]) @@ -1416,7 +1416,7 @@ (emojify . [(20210108 1111) ((seq (1 11)) (ht (2 0)) (emacs (24 3))) "Display emojis in Emacs" tar ((:commit . "cfa00865388809363df3f884b4dd554a5d44f835") (:authors ("Iqbal Ansari" . "iqbalansari02@yahoo.com")) (:maintainers ("Iqbal Ansari" . "iqbalansari02@yahoo.com")) (:maintainer "Iqbal Ansari" . "iqbalansari02@yahoo.com") (:keywords "multimedia" "convenience") (:url . "https://github.com/iqbalansari/emacs-emojify"))]) (emojify-logos . [(20180814 917) ((emojify (0 4))) "Add logos to emojify" tar ((:commit . "a3e78bcbdf863092d4c9b026ac08bf7d1c7c0e8b") (:authors ("mxgoldstein" . "m_goldstein@gmx.net")) (:maintainers ("mxgoldstein" . "m_goldstein@gmx.net")) (:maintainer "mxgoldstein" . "m_goldstein@gmx.net") (:url . "https://github.com/mxgoldstein/emojify-logos"))]) (empos . [(20151011 1916) nil "Locate bibtex citations from within emacs" tar ((:commit . "7b99ad30e56937adb7e6349777e5a2045597d564") (:authors ("Dimitris Alikaniotis" . "da352[at]cam.ac.uk")) (:maintainers ("Dimitris Alikaniotis" . "da352[at]cam.ac.uk")) (:maintainer "Dimitris Alikaniotis" . "da352[at]cam.ac.uk") (:keywords "citations" "reference" "bibtex" "reftex") (:url . "http://github.com/dimalik/empos/"))]) - (empv . [(20240728 1657) ((emacs (28 1)) (s (1 13 0)) (compat (29 1 4 4))) "A multimedia player/manager, YouTube interface" tar ((:commit . "1a8b93b72cc574355150edf984f1fc896db63921") (: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/empv.el"))]) + (empv . [(20240810 2302) ((emacs (28 1)) (s (1 13 0)) (compat (29 1 4 4))) "A multimedia player/manager, YouTube interface" tar ((:commit . "165bf678b5c27e402ee95e58471748480aab1657") (: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/empv.el"))]) (emr . [(20220108 548) ((s (1 3 1)) (dash (1 2 0)) (cl-lib (0 2)) (popup (0 5 0)) (emacs (24 1)) (list-utils (0 3 0)) (paredit (24 0 0)) (projectile (0 9 1)) (clang-format (0 0 1)) (iedit (0 97))) "Emacs refactoring system." tar ((:commit . "cac1b52932926f56d7f6d2923732d20bbd20670d") (:authors ("Chris Barrett" . "chris.d.barrett@me.com")) (:maintainers ("Chris Barrett" . "chris.d.barrett@me.com")) (:maintainer "Chris Barrett" . "chris.d.barrett@me.com") (:keywords "tools" "convenience" "refactoring") (:url . "https://github.com/Wilfred/emacs-refactor"))]) (enclose . [(20121008 1614) nil "Enclose cursor within punctuation pairs." tar ((:commit . "2fff3d4fcc1089f87647042d7164ba04282766ae") (:authors ("Johan Andersson" . "johan.rejeep@gmail.com")) (:maintainer "Johan Andersson" . "johan.rejeep@gmail.com") (:keywords "speed" "convenience") (:url . "http://github.com/rejeep/enclose"))]) (encourage-mode . [(20151128 905) ((emacs (24 4))) "Encourages you in your work. :D" tar ((:commit . "ca411e6bfd3d0edffe95852127bd995730b942e3") (:authors ("Patrick Mosby" . "patrick@schreiblogade.de")) (:maintainers ("Patrick Mosby" . "patrick@schreiblogade.de")) (:maintainer "Patrick Mosby" . "patrick@schreiblogade.de") (:keywords "fun") (:url . "https://github.com/halbtuerke/encourage-mode.el"))]) @@ -1544,7 +1544,7 @@ (evil-cleverparens . [(20240529 1025) ((evil (1 0)) (paredit (1)) (smartparens (1 6 1)) (emacs (24 4)) (dash (2 12 0))) "Evil friendly minor-mode for editing lisp." tar ((:commit . "6637717af0bdac55f97eef98433d53a10395cf77") (:authors ("Olli Piepponen" . "opieppo@gmail.com")) (:maintainers ("Olli Piepponen" . "opieppo@gmail.com")) (:maintainer "Olli Piepponen" . "opieppo@gmail.com") (:keywords "convenience" "emulations") (:url . "https://github.com/emacs-evil/evil-cleverparens"))]) (evil-colemak-basics . [(20221230 1443) ((emacs (24 3)) (evil (1 2 12)) (evil-snipe (2 0 3))) "Basic Colemak key bindings for evil-mode" tar ((:commit . "ea519b962f051cabced8aadaf6ed0134b861225c") (:authors ("Wouter Bolsterlee" . "wouter@bolsterl.ee")) (:maintainers ("Wouter Bolsterlee" . "wouter@bolsterl.ee")) (:maintainer "Wouter Bolsterlee" . "wouter@bolsterl.ee") (:keywords "convenience" "emulations" "colemak" "evil") (:url . "https://github.com/wbolster/evil-colemak-basics"))]) (evil-colemak-minimal . [(20171006 1317) ((emacs (24)) (evil (1 2 12))) "Minimal Colemak key bindings for evil-mode" tar ((:commit . "6d98b6da60f414524a0d718f76024c26dce742b3") (:authors ("Bryan Allred" . "bryan@revolvingcow.com")) (:maintainers ("Bryan Allred" . "bryan@revolvingcow.com")) (:maintainer "Bryan Allred" . "bryan@revolvingcow.com") (:keywords "colemak" "evil") (:url . "https://github.com/bmallred/evil-colemak-minimal"))]) - (evil-collection . [(20240805 1557) ((emacs (26 3)) (evil (1 2 13)) (annalist (1 0))) "A set of keybindings for Evil mode" tar ((:commit . "e49d8e96ccc83d2bf7583c124582fc1ef076b15c") (: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 . [(20240813 1850) ((emacs (26 3)) (evil (1 2 13)) (annalist (1 0))) "A set of keybindings for Evil mode" tar ((:commit . "fd233059fba8daaab2bd9bb396fc756af56a296d") (: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") (: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"))]) @@ -1638,7 +1638,7 @@ (exsqlaim-mode . [(20170607 1003) ((s (1 10 0))) "Use variables inside sql queries" tar ((:commit . "a2e0a62ec8b87193d8eaa695774bfd689324b06c") (:authors ("Ahmad Nazir Raja" . "ahmadnazir@gmail.com")) (:maintainers ("Ahmad Nazir Raja" . "ahmadnazir@gmail.com")) (:maintainer "Ahmad Nazir Raja" . "ahmadnazir@gmail.com") (:url . "https://github.com/ahmadnazir/exsqlaim-mode"))]) (extempore-mode . [(20220704 2241) ((emacs (24 4))) "Emacs major mode for Extempore source files" tar ((:commit . "92e0fff482a0a4dc2971c39581c5ea9e84ae5e1c") (:authors ("Ben Swift" . "ben@benswift.me")) (:maintainers ("Ben Swift" . "ben@benswift.me")) (:maintainer "Ben Swift" . "ben@benswift.me") (:keywords "extempore") (:url . "http://github.com/extemporelang/extempore-emacs-mode"))]) (extend-dnd . [(20151122 1850) nil "R drag and Drop" tar ((:commit . "80c966c93b82c9bb5c6225a432557c39144fc602") (: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"))]) + (external-dict . [(20240813 1416) ((emacs (25 1))) "Query external dictionary like goldendict, Bob.app etc" tar ((:commit . "bfa4135c53306f1d57c721ae9ce37f1013052192") (: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 . [(20240502 431) ((s (1 11 0)) (emacs (24 3)) (f (0 20 0)) (transient (0 3 6)) (project (0 9 8))) "ExUnit test runner" tar ((:commit . "b6134ce920a4bbc561f65fac1d1bf37206d97505") (: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 . [(20240418 2142) ((emacs (27 1))) "Edit mode for EXWM" tar ((:commit . "046b8c11f71bfd6c798df770c6b7708af2c187a2") (:keywords "convenience") (:url . "https://github.com/agzam/exwm-edit"))]) @@ -1768,7 +1768,7 @@ (flycheck-cask . [(20240205 1721) ((emacs (24 3)) (flycheck (0 14)) (dash (2 4 0))) "Cask support in Flycheck" tar ((:commit . "0eeec5197e9d31bfcfc39380b262d65259a87d91") (:authors ("Sebastian Wiesner" . "swiesner@lunaryorn.com")) (:maintainers ("Sebastian Wiesner" . "swiesner@lunaryorn.com")) (:maintainer "Sebastian Wiesner" . "swiesner@lunaryorn.com") (:keywords "tools" "convenience") (:url . "https://github.com/flycheck/flycheck-cask"))]) (flycheck-cfn . [(20240512 2341) ((emacs (27 0)) (flycheck (31))) "Flycheck backend for AWS cloudformation" tar ((:commit . "b26a95a219aa700256b22fd026cace57bce1701b") (:authors ("William Orr" . "will@worrbase.com")) (:maintainers ("William Orr" . "will@worrbase.com")) (:maintainer "William Orr" . "will@worrbase.com") (:keywords "convenience") (:url . "https://gitlab.com/worr/cfn-mode"))]) (flycheck-checkbashisms . [(20230313 1418) ((emacs (24)) (flycheck (0 25))) "checkbashisms checker for flycheck" tar ((:commit . "ca8f11679c77d6702f34e773bdde185ceb47a05d") (:authors ("Cuong Le" . "cuong.manhle.vn@gmail.com")) (:maintainers ("Cuong Le" . "cuong.manhle.vn@gmail.com")) (:maintainer "Cuong Le" . "cuong.manhle.vn@gmail.com") (:keywords "convenience" "tools" "sh" "unix") (:url . "https://github.com/cuonglm/flycheck-checkbashisms"))]) - (flycheck-checkpatch . [(20170217 1025) ((emacs (25)) (flycheck (30))) "Flycheck support for checkpatch.pl tool" tar ((:commit . "6461fc7b0d493eb9863814055f8bce5fa35739de") (:authors ("Alexander Yarygin" . "yarygin.alexander@gmail.com")) (:maintainers ("Alexander Yarygin" . "yarygin.alexander@gmail.com")) (:maintainer "Alexander Yarygin" . "yarygin.alexander@gmail.com") (:url . "https://github.com/zpp0/flycheck-checkpatch"))]) + (flycheck-checkpatch . [(20240810 1951) ((emacs (25)) (flycheck (30))) "Flycheck support for checkpatch.pl tool" tar ((:commit . "61710dff2828ff119968161e7118fce2a4a0b67f") (:authors ("Alexander Yarygin" . "yarygin.alexander@gmail.com")) (:maintainers ("Alexander Yarygin" . "yarygin.alexander@gmail.com")) (:maintainer "Alexander Yarygin" . "yarygin.alexander@gmail.com") (:url . "https://github.com/zpp0/flycheck-checkpatch"))]) (flycheck-clang-analyzer . [(20211214 648) ((flycheck (0 24)) (emacs (24 4))) "Integrate Clang Analyzer with flycheck" tar ((:commit . "646d9f3a80046ab231a07526778695d5decad92d") (: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/flycheck-clang-analyzer"))]) (flycheck-clang-tidy . [(20201115 1232) ((flycheck (0 30))) "Flycheck syntax checker using clang-tidy" tar ((:commit . "3bd947fb0dcc1e97617eab7be9e1b6e57db5e091") (:authors ("Sebastian Nagel" . "sebastian.nagel@ncoding.at")) (:maintainers ("tastytea" . "tastytea@tastytea.de")) (:maintainer "tastytea" . "tastytea@tastytea.de") (:keywords "convenience" "languages" "tools") (:url . "https://github.com/ch1bo/flycheck-clang-tidy"))]) (flycheck-clangcheck . [(20150712 710) ((cl-lib (0 5)) (seq (1 7)) (flycheck (0 17))) "A Flycheck checker difinition for ClangCheck." tar ((:commit . "24a9424c484420073a24443a829fd5779752362b") (:authors ("kumar8600" . "kumar8600@gmail.com")) (:maintainers ("kumar8600" . "kumar8600@gmail.com")) (:maintainer "kumar8600" . "kumar8600@gmail.com") (:url . "https://github.com/kumar8600/flycheck-clangcheck"))]) @@ -1962,7 +1962,7 @@ (foreign-regexp . [(20200325 50) nil "search and replace by foreign regexp." tar ((:commit . "e2dd47f2160cadc194eb156e7c76c3c869e6706e") (:authors ("K-talo Miyazaki" . "KeitarodotMiyazakiatgmaildotcom")) (:maintainers ("K-talo Miyazaki" . "KeitarodotMiyazakiatgmaildotcom")) (:maintainer "K-talo Miyazaki" . "KeitarodotMiyazakiatgmaildotcom") (: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"))]) - (forge . [(20240808 1943) ((emacs (27 1)) (compat (30 0 0 0)) (closql (2 0 0)) (dash (2 19 1)) (emacsql (4 0 0)) (ghub (4 0 0)) (let-alist (1 0 6)) (magit (4 0 0)) (markdown-mode (2 6)) (seq (2 24)) (transient (0 7 4)) (yaml (0 5 5))) "Access Git forges from Magit." tar ((:commit . "0b5571b40e544bd182e5ed76d2400fe2fa8716d1") (:authors ("Jonas Bernoulli" . "emacs.forge@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.forge@jonas.bernoulli.dev") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/forge"))]) + (forge . [(20240813 156) ((emacs (27 1)) (compat (30 0 0 0)) (closql (2 0 0)) (dash (2 19 1)) (emacsql (4 0 0)) (ghub (4 0 0)) (let-alist (1 0 6)) (magit (4 0 0)) (markdown-mode (2 6)) (seq (2 24)) (transient (0 7 4)) (yaml (0 5 5))) "Access Git forges from Magit." tar ((:commit . "2f5e03f18d604806dd6be090e419b11bceffdece") (:authors ("Jonas Bernoulli" . "emacs.forge@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.forge@jonas.bernoulli.dev") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/forge"))]) (form-feed . [(20210508 1627) ((emacs (24 1))) "Display ^L glyphs as horizontal lines" tar ((:commit . "ac1f0ef30a11979f5dfe12d8c05a666739e486ff") (:authors ("Vasilij Schneidermann" . "mail@vasilij.de")) (:maintainers ("Vasilij Schneidermann" . "mail@vasilij.de")) (:maintainer "Vasilij Schneidermann" . "mail@vasilij.de") (:keywords "faces") (:url . "https://depp.brause.cc/form-feed"))]) (form-feed-st . [(20231002 2211) ((emacs (25 1))) "Display ^L glyphs as full-width horizontal lines" tar ((:commit . "f91c8daf35b7588e0aa24c8716c8cfd8ff0067c8") (:keywords "faces") (:url . "https://github.com/leodag/form-feed-st"))]) (format-all . [(20240511 1811) ((emacs (24 4)) (inheritenv (0 1)) (language-id (0 20))) "Auto-format C, C++, JS, Python, Ruby and 50 other languages" tar ((:commit . "c5ddfc5f3317eaa2a7541a818a0fce961e5e61dd") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainers ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "languages" "util") (:url . "https://github.com/lassik/emacs-format-all-the-code"))]) @@ -2034,7 +2034,7 @@ (geiser-chicken . [(20220717 1130) ((emacs (24 4)) (geiser (0 19))) "Chicken's implementation of the geiser protocols" tar ((:commit . "a480598b5908c95bc8d3178a48f13e9072a9235b") (:keywords "languages" "chicken" "scheme" "geiser") (:url . "https://gitlab.com/emacs-geiser/chicken"))]) (geiser-gambit . [(20220208 1356) ((emacs (26 1)) (geiser (0 18))) "Gambit's implementation of the geiser protocols" tar ((:commit . "381d74ca5059b44fe3d8b5daf42214019c6d1a88") (:maintainers ("Jose A Ortega Ruiz" . "jao@gnu.org")) (:maintainer "Jose A Ortega Ruiz" . "jao@gnu.org") (:keywords "languages" "gambit" "scheme" "geiser") (:url . "https://gitlab.com/emacs-geiser/gambit"))]) (geiser-gauche . [(20220503 1700) ((emacs (26 1)) (geiser (0 11 2))) "Gauche scheme support for Geiser" tar ((:commit . "8ff743f6416f00751e24aef8b9791501a40f5421") (:authors ("András Simonyi" . "andras.simonyi@gmail.com")) (:maintainers ("András Simonyi" . "andras.simonyi@gmail.com")) (:maintainer "András Simonyi" . "andras.simonyi@gmail.com") (:keywords "languages" "gauche" "scheme" "geiser") (:url . "https://gitlab.com/emacs-geiser/gauche"))]) - (geiser-guile . [(20240712 1202) ((emacs (25 1)) (transient (0 3)) (geiser (0 28 1))) "Guile and Geiser talk to each other" tar ((:commit . "ebdd1923b0780778706ea6b16aa2b0ce3e7dc33d") (:authors ("Jose Antonio Ortega Ruiz" . "(jao@gnu.org)")) (:maintainers ("Jose Antonio Ortega Ruiz" . "(jao@gnu.org)")) (:maintainer "Jose Antonio Ortega Ruiz" . "(jao@gnu.org)") (:keywords "languages" "guile" "scheme" "geiser") (:url . "https://gitlab.com/emacs-geiser/guile"))]) + (geiser-guile . [(20240811 1520) ((emacs (26 1)) (transient (0 3)) (geiser (0 28 1))) "Guile and Geiser talk to each other" tar ((:commit . "5a856c2982030ff77e2d151ead4fcd991512f362") (:authors ("Jose Antonio Ortega Ruiz" . "(jao@gnu.org)")) (:maintainers ("Jose Antonio Ortega Ruiz" . "(jao@gnu.org)")) (:maintainer "Jose Antonio Ortega Ruiz" . "(jao@gnu.org)") (:keywords "languages" "guile" "scheme" "geiser") (:url . "https://gitlab.com/emacs-geiser/guile"))]) (geiser-kawa . [(20210920 1607) ((emacs (26 1)) (geiser (0 16))) "Kawa scheme support for Geiser" tar ((:commit . "5896b19642923f74f718eb68d447560b2d26d797") (:authors ("spellcard199" . "spellcard199@protonmail.com")) (:maintainers ("spellcard199" . "spellcard199@protonmail.com")) (:maintainer "spellcard199" . "spellcard199@protonmail.com") (:keywords "languages" "kawa" "scheme" "geiser") (:url . "https://gitlab.com/emacs-geiser/kawa"))]) (geiser-mit . [(20211204 1935) ((emacs (24 4)) (geiser (0 18))) "MIT/GNU Scheme's implementation of the geiser protocols" tar ((:commit . "4e90e9ae815e89f3540fb9644e6016c663ef5765") (:authors ("Peter" . "craven@gmx.net")) (:maintainers ("Jose A Ortega Ruiz" . "jao@gnu.org")) (:maintainer "Jose A Ortega Ruiz" . "jao@gnu.org") (:keywords "languages" "mit" "scheme" "geiser") (:url . "https://gitlab.com/emacs-geiser/mit"))]) (geiser-racket . [(20210421 125) ((emacs (26 1)) (geiser (0 16))) "Support for Racket in Geiser" tar ((:commit . "22e56ce80389544d3872cf4beb4008fb514b2218") (:authors ("Jose Antonio Ortega Ruiz" . "(jao@gnu.org)")) (:maintainers ("Jose Antonio Ortega Ruiz" . "(jao@gnu.org)")) (:maintainer "Jose Antonio Ortega Ruiz" . "(jao@gnu.org)") (:keywords "languages" "racket" "scheme" "geiser") (:url . "https://gitlab.com/emacs-geiser/racket"))]) @@ -2078,7 +2078,7 @@ (git-blamed . [(20161028 1926) nil "Minor mode for incremental blame for Git" tar ((:commit . "cef196abf398e2dd11f775d1e6cd8690567408aa") (:keywords "git" "version control" "release management"))]) (git-cliff . [(20240730 1912) ((emacs (29 1)) (transient (0 6 0)) (dash (2 19 1))) "Generate and update changelog using git-cliff" tar ((:commit . "514396c9306338f7df49140e3c9d2177ad7f3567") (:authors ("liuyinz" . "liuyinz95@gmail.com")) (:maintainers ("liuyinz" . "liuyinz95@gmail.com")) (:maintainer "liuyinz" . "liuyinz95@gmail.com") (:keywords "tools") (:url . "https://github.com/liuyinz/git-cliff.el"))]) (git-command . [(20191028 333) ((term-run (0 1 4)) (with-editor (2 3 1))) "A Git Command-Line interface" tar ((:commit . "a773d40da39dfb1c6ecf2b0758aa370ddea8f06d") (:authors ("10sr" . "8slashes+el[at]gmail[dot]com")) (:maintainers ("10sr" . "8slashes+el[at]gmail[dot]com")) (:maintainer "10sr" . "8slashes+el[at]gmail[dot]com") (:keywords "utility" "git") (:url . "https://github.com/10sr/git-command-el"))]) - (git-commit . [(20240808 1852) ((emacs (26 1)) (compat (30 0 0 0)) (transient (0 7 4)) (with-editor (3 4 1))) "Edit Git commit messages." tar ((:commit . "020aca7c9c4154dbc4a72acbd56165ecccea1bf1") (:authors ("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") ("Sebastian Wiesner" . "lunaryorn@gmail.com") ("Florian Ragwitz" . "rafl@debian.org") ("Marius Vollmer" . "marius.vollmer@gmail.com")) (:maintainer "Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/magit"))]) + (git-commit . [(20240811 1419) ((emacs (26 1)) (compat (30 0 0 0)) (transient (20240805)) (with-editor (20240806))) "Edit Git commit messages." tar ((:commit . "a2739d7db1fdf19b95f36f6ddd15b0c1f523bd26") (:authors ("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") ("Sebastian Wiesner" . "lunaryorn@gmail.com") ("Florian Ragwitz" . "rafl@debian.org") ("Marius Vollmer" . "marius.vollmer@gmail.com")) (:maintainer "Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/magit"))]) (git-commit-insert-issue . [(20230512 1416) ((emacs (25)) (projectile (0)) (s (0)) (ghub (0)) (bitbucket (0))) "Get issues list when typing \"Fixes #\"" tar ((:commit . "df7ce0549d1db7bab27d401a351ea0d187c4a673") (:keywords "tools" "vc" "github" "gitlab" "bitbucket" "commit" "issues") (:url . "https://gitlab.com/emacs-stuff/git-commit-insert-issue/"))]) (git-commit-ts-mode . [(20240722 1815) ((emacs (29 1))) "Tree-sitter support for Git commit messages" tar ((:commit . "ba722ba44964e87b2acf48cb7b6154ad4c99bc26") (:authors ("Daniil Shvalov" . "daniil.shvalov@gmail.com")) (:maintainers ("Daniil Shvalov" . "daniil.shvalov@gmail.com")) (:maintainer "Daniil Shvalov" . "daniil.shvalov@gmail.com") (:keywords "tree-sitter" "git" "faces") (:url . "https://github.com/danilshvalov/git-commit-ts-mode"))]) (git-dwim . [(20170126 1214) nil "Context-aware git commands such as branch handling" tar ((:commit . "485c732130686c2f28a026e385366006435394b9") (:authors ("rubikitch" . "rubikitch@ruby-lang.org")) (:maintainers ("rubikitch" . "rubikitch@ruby-lang.org")) (:maintainer "rubikitch" . "rubikitch@ruby-lang.org") (:keywords "git" "tools" "convenience") (:url . "http://www.emacswiki.org/cgi-bin/wiki/download/git-dwim.el"))]) @@ -2136,7 +2136,7 @@ (gnome-calendar . [(20161110 1256) nil "Integration with the GNOME Shell calendar" tar ((:commit . "668591bec95c23934c5e1ef100cec4824e7cb25d") (:authors ("Nicolas Petton" . "nicolas@petton.fr")) (:maintainers ("Nicolas Petton" . "nicolas@petton.fr")) (:maintainer "Nicolas Petton" . "nicolas@petton.fr") (:keywords "gnome" "calendar"))]) (gnome-screencast . [(20210125 2001) ((emacs (25))) "Use Gnome screen recording functionality using elisp" tar ((:commit . "1f4ef60fe9d452320dc02f89e289bac04ef2ad1c") (: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 "tools" "multimedia") (:url . "https://github.com/juergenhoetzel/emacs-gnome-screencast"))]) (gnomenm . [(20150316 1918) ((s (1 9 0)) (dash (2 3 0)) (kv (0 0 19))) "Emacs interface to Gnome nmcli command" tar ((:commit . "9065cda44ffc9e06239b8189a0154d31314c3b4d") (:authors ("Nic Ferrier" . "nferrier@ferrier.me.uk")) (:maintainers ("Nic Ferrier" . "nferrier@ferrier.me.uk")) (:maintainer "Nic Ferrier" . "nferrier@ferrier.me.uk") (:keywords "processes" "hardware") (:url . "http://github.com/nicferrier/emacs-nm"))]) - (gnosis . [(20240807 1834) ((emacs (27 2)) (emacsql (20240124)) (compat (29 1 4 2)) (transient (0 7 2))) "Spaced Repetition System" tar ((:commit . "1c3eafb6efb05d52a8b01c463d1b47efc1fb2c8e") (:authors ("Thanos Apollo" . "public@thanosapollo.org")) (:maintainers ("Thanos Apollo" . "public@thanosapollo.org")) (:maintainer "Thanos Apollo" . "public@thanosapollo.org") (:keywords "extensions") (:url . "https://thanosapollo.org/projects/gnosis"))]) + (gnosis . [(20240811 2007) ((emacs (27 2)) (emacsql (20240124)) (compat (29 1 4 2)) (transient (0 7 2))) "Spaced Repetition System" tar ((:commit . "fe019d58f26e99e59a5601d2e276ad06676b1b66") (:authors ("Thanos Apollo" . "public@thanosapollo.org")) (:maintainers ("Thanos Apollo" . "public@thanosapollo.org")) (:maintainer "Thanos Apollo" . "public@thanosapollo.org") (:keywords "extensions") (:url . "https://thanosapollo.org/projects/gnosis"))]) (gntp . [(20141025 250) nil "Growl Notification Protocol for Emacs" tar ((:commit . "767571135e2c0985944017dc59b0be79af222ef5") (:authors ("Engelke Eschner" . "tekai@gmx.li")) (:maintainers ("Engelke Eschner" . "tekai@gmx.li")) (:maintainer "Engelke Eschner" . "tekai@gmx.li"))]) (gnu-apl-mode . [(20220404 341) ((emacs (27))) "Integrate GNU APL with Emacs" tar ((:commit . "c8695b0d55b5167263a843252ffd21a589018427") (:authors ("Elias Mårtenson" . "lokedhs@gmail.com")) (:maintainer "Elias Mårtenson" . "lokedhs@gmail.com") (:keywords "languages") (:url . "http://www.gnu.org/software/apl/"))]) (gnu-indent . [(20221127 2112) ((emacs (25 1))) "Indent your code with GNU Indent" tar ((:commit . "f31dbe60478b6270bb57b6b05998df8eec56f801") (:authors ("Akib Azmain Turja" . "akib@disroot.org")) (:maintainers ("Akib Azmain Turja" . "akib@disroot.org")) (:maintainer "Akib Azmain Turja" . "akib@disroot.org") (:keywords "tools" "c") (:url . "https://codeberg.org/akib/emacs-gnu-indent"))]) @@ -2178,7 +2178,7 @@ (go-tag . [(20230111 651) ((emacs (24 0)) (go-mode (1 5 0))) "Edit Golang struct field tag" tar ((:commit . "33f2059551d5298ca228d90f525b99d1a8d70364") (:authors ("Brantou" . "brantou89@gmail.com")) (:maintainers ("Brantou" . "brantou89@gmail.com")) (:maintainer "Brantou" . "brantou89@gmail.com") (:keywords "tools") (:url . "https://github.com/brantou/emacs-go-tag"))]) (go-translate . [(20240802 131) ((emacs (28 1))) "Translation framework, configurable and scalable" tar ((:commit . "424682b94df9efb3dd59e396677b45e4a67b7723") (:authors ("lorniu" . "lorniu@gmail.com")) (:maintainers ("lorniu" . "lorniu@gmail.com")) (:maintainer "lorniu" . "lorniu@gmail.com") (:keywords "convenience") (:url . "https://github.com/lorniu/go-translate"))]) (gobgen . [(20161020 1523) ((emacs (24 4))) "Generate GObject descendants using a detailed form" tar ((:commit . "ed2c2b0d217deae293096f3cf14aa492791ddd4f") (:authors ("Gergely Polonkai" . "gergely@polonkai.eu")) (:maintainers ("Gergely Polonkai" . "gergely@polonkai.eu")) (:maintainer "Gergely Polonkai" . "gergely@polonkai.eu") (:keywords "gobject" "glib" "gtk" "helper" "utilities"))]) - (god-mode . [(20221230 708) ((emacs (25 1))) "Minor mode for God-like command entering" tar ((:commit . "607aff10a7b27a8aa0c1a15c2c39337ab17cfda7") (:authors ("Chris Done" . "chrisdone@gmail.com")) (:maintainers ("Chris Done" . "chrisdone@gmail.com")) (:maintainer "Chris Done" . "chrisdone@gmail.com") (:url . "https://github.com/emacsorphanage/god-mode"))]) + (god-mode . [(20240812 1014) ((emacs (26 3))) "Minor mode for God-like command entering" tar ((:commit . "55490fffd42ae7d4671ce409214a3fcd0868d791") (:authors ("Chris Done" . "chrisdone@gmail.com")) (:maintainers ("Chris Done" . "chrisdone@gmail.com")) (:maintainer "Chris Done" . "chrisdone@gmail.com") (:url . "https://github.com/emacsorphanage/god-mode"))]) (godoctor . [(20180710 2152) nil "Frontend for godoctor" tar ((:commit . "4b45ff3d0572f0e84056e4c3ba91fcc178199859") (:authors ("Sangho Na" . "microamp@protonmail.com")) (:maintainers ("Sangho Na" . "microamp@protonmail.com")) (:maintainer "Sangho Na" . "microamp@protonmail.com") (:keywords "go" "golang" "refactoring") (:url . "https://github.com/microamp/godoctor.el"))]) (gofmt-tag . [(20240111 2031) ((emacs (27))) "Format and align go struct tags" tar ((:commit . "b7cc315ac45342fc9c17dde779cc9c37aa309841") (:authors ("ybenel" . "http://github/m1ndo")) (:maintainers ("ybenel" . "root@ybenel.cf")) (:maintainer "ybenel" . "root@ybenel.cf") (:keywords "tools" "wp" "matching") (:url . "https://github.com/m1ndo/gofmt-tag"))]) (goggles . [(20240216 1542) ((emacs (27 1))) "Pulse modified regions" tar ((:commit . "41d3669d7ae7b73bd39d298e5373ece48b656ce3") (: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/goggles"))]) @@ -2309,7 +2309,7 @@ (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") (:keywords "heex" "languages" "tree-sitter") (:url . "https://github.com/wkirschbaum/elixir-ts-mode"))]) (helix-theme . [(20240729 1203) nil "Color theme inspired by Helix editor's default colors" tar ((:commit . "fe1c95a93142020338a7de2a7984c5cd0018fbf4") (:keywords "faces") (:url . "https://github.com/ibakepunk/helix-theme"))]) - (helm . [(20240806 747) ((helm-core (3 9 9)) (wfnames (1 2))) "Helm is an Emacs incremental and narrowing framework" tar ((:commit . "2908dc8da19950ee2f9c1ec13862e2ff9305b8b6") (: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 . [(20240812 1744) ((helm-core (3 9 9)) (wfnames (1 2))) "Helm is an Emacs incremental and narrowing framework" tar ((:commit . "dc745dd55362471115f919bebc554f5c26da85f2") (: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")) (: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"))]) @@ -2323,7 +2323,7 @@ (helm-bibtexkey . [(20140214 1504) ((helm (1 5 8))) "Bibtexkey source for helm" tar ((:commit . "aa1637ea5c8c5f1817e480fc2a3750cafab3d99f") (:authors ("TAKAGI Kentaro" . "kentaro0910_at_gmail.com")) (:maintainers ("TAKAGI Kentaro" . "kentaro0910_at_gmail.com")) (:maintainer "TAKAGI Kentaro" . "kentaro0910_at_gmail.com") (:keywords "bib" "tex") (:url . "https://github.com/kenbeese/helm-bibtexkey"))]) (helm-bind-key . [(20141109 515) ((bind-key (1 0)) (helm (1 6 4))) "helm-source for for bind-key." tar ((:commit . "9da6ad8b7530e72fb4ac67be8c6a482898dddc25") (:authors ("Yuhei Maeda" . "yuhei.maeda_at_gmail.com")) (:keywords "convenience" "emulation"))]) (helm-bitbucket . [(20220722 1538) ((emacs (24)) (helm-core (3 6 0))) "Search Bitbucket with Helm" tar ((:commit . "9d07a274584ad364a2620c6389f86d90502f2640") (:authors ("Peter Urbak" . "tolowercase@gmail.com")) (:maintainers ("Peter Urbak" . "tolowercase@gmail.com")) (:maintainer "Peter Urbak" . "tolowercase@gmail.com") (:keywords "matching") (:url . "https://github.com/dragonwasrobot/helm-bitbucket"))]) - (helm-bm . [(20240722 1430) ((bm (1 0)) (cl-lib (0 5)) (helm (1 9 3))) "helm sources for bm.el" tar ((:commit . "9a34b29d5a034e97f509046f63df66a89278416b") (:authors ("Yasuyuki Oka" . "yasuyk@gmail.com")) (:maintainers ("Yasuyuki Oka" . "yasuyk@gmail.com")) (:maintainer "Yasuyuki Oka" . "yasuyk@gmail.com") (:keywords "helm" "bookmark") (:url . "https://github.com/yasuyk/helm-bm"))]) + (helm-bm . [(20240812 1738) ((bm (1 0)) (cl-lib (0 5)) (helm (1 9 3))) "helm sources for bm.el" tar ((:commit . "4744b5784df5800f36c3c54de5269034191155f5") (:authors ("Yasuyuki Oka" . "yasuyk@gmail.com") ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainers ("Thierry Volpiatto" . "thievol@posteo.net")) (:maintainer "Thierry Volpiatto" . "thievol@posteo.net") (:keywords "helm" "bookmark") (:url . "https://github.com/emacs-helm/helm-bm"))]) (helm-books . [(20170325 631) ((helm (1 7 7))) "Helm interface for searching books" tar ((:commit . "6735e1787f99b5ef77b276fa5c43e565b4d3e792") (:authors ("grugrut" . "grugruglut+github@gmail.com")) (:maintainers ("grugrut" . "grugruglut+github@gmail.com")) (:maintainer "grugrut" . "grugruglut+github@gmail.com") (:url . "https://github.com/grugrut/helm-books"))]) (helm-bufler . [(20230916 916) ((emacs (26 3)) (bufler (0 2 -1)) (helm (1 9 4))) "Helm source for Bufler" tar ((:commit . "938b186f09739196fe0e65e8e370f90b47008054") (: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/bufler.el"))]) (helm-bundle-show . [(20190526 1401) ((emacs (24)) (helm (1 8 0))) "Bundle show with helm interface" tar ((:commit . "70f1ca7d1847c7d5cd5a3e488562cd4a295b809f") (: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-bundle-show"))]) @@ -2343,7 +2343,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 . [(20240802 1013) ((emacs (25 1)) (async (1 9 8))) "Development files for Helm" tar ((:commit . "d2497a8ceae2d99b20135569d1aab75f0a47d936") (: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 . [(20240813 1920) ((emacs (25 1)) (async (1 9 8))) "Development files for Helm" tar ((:commit . "8eead4b760e5b5a14e917cd0a5103705164e1536") (: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") (: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"))]) @@ -2407,7 +2407,7 @@ (helm-lib-babel . [(20180510 1324) ((cl-lib (0 5)) (helm (1 9 2)) (emacs (24 4))) "helm insertion of babel function references" tar ((:commit . "41bc0cdea8a604c6c8dc83ed5066644d33688fad") (: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/helm-lib-babel.el"))]) (helm-lines . [(20220103 1909) ((emacs (24 4)) (helm (1 9 8))) "A helm interface for completing by lines" tar ((:commit . "f5ad178818d223f32a0bf60d370b50c01df5f3da") (:authors (nil . "@torgeir")) (:maintainers (nil . "@torgeir")) (:maintainer nil . "@torgeir") (:keywords "files" "helm" "rg" "ag" "pt" "vc" "git" "lines" "complete" "tools" "languages") (:url . "https://github.com/torgeir/helm-lines.el/"))]) (helm-lobsters . [(20230820 1403) ((helm (1 0)) (cl-lib (0 5))) "helm front-end for lobste.rs" tar ((:commit . "3a1af0d063ca24fe0187daff12110171b942c7d3") (:authors ("Julien BLANCHARD" . "julien@sideburns.eu")) (:maintainers ("Julien BLANCHARD" . "julien@sideburns.eu")) (:maintainer "Julien BLANCHARD" . "julien@sideburns.eu") (:url . "https://github.com/julienXX/helm-lobste.rs"))]) - (helm-ls-git . [(20240810 1145) ((helm (3 9 5)) (emacs (25 3))) "list git files." tar ((:commit . "78bc27b61faf82a0adf815aa5aa8616f95bca48d") (:url . "https://github.com/emacs-helm/helm-ls-git"))]) + (helm-ls-git . [(20240811 1739) ((helm (3 9 5)) (emacs (25 3))) "list git files." tar ((:commit . "244b6854c1c118e41c8fac0201eff03ba034ddc6") (:url . "https://github.com/emacs-helm/helm-ls-git"))]) (helm-ls-hg . [(20150909 543) ((helm (1 7 8))) "List hg files in hg project." tar ((:commit . "61b91a22fcfb62d0fc56e361ec01ce96973c7165"))]) (helm-ls-svn . [(20190316 2203) ((emacs (24 1)) (helm (1 7 0)) (cl-lib (0 5))) "helm extension to list svn files" tar ((:commit . "a6043e1187282f649e2cb9f0e722a42daf41294b") (:authors ("Chunyang Xu" . "chunyang@macports.org")) (:maintainers ("Chunyang Xu" . "chunyang@macports.org")) (:maintainer "Chunyang Xu" . "chunyang@macports.org") (:keywords "helm" "svn") (:url . "https://svn.macports.org/repository/macports/users/chunyang/helm-ls-svn.el/helm-ls-svn.el"))]) (helm-lsp . [(20210419 2014) ((emacs (25 1)) (dash (2 14 1)) (lsp-mode (5 0)) (helm (2 0))) "LSP helm integration" tar ((:commit . "c2c6974dadfac459b1a69a1217441283874cea92") (: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/yyoncho/helm-lsp"))]) @@ -2595,8 +2595,8 @@ (hydandata-light-theme . [(20190809 1925) nil "A light color theme that is easy on your eyes" tar ((:commit . "812ffa4bee3163098ef66ee4506feed45018be4e") (:authors ("David Chkhikvadze" . "david@chkhd.net")) (:maintainers ("David Chkhikvadze" . "david@chkhd.net")) (:maintainer "David Chkhikvadze" . "david@chkhd.net") (:keywords "color-theme" "theme") (:url . "https://github.com/chkhd/hydandata-light-theme"))]) (hyde . [(20160508 308) nil "Major mode to help create and manage Jekyll blogs" tar ((:commit . "a8cd6ed00ecd8d7de0ded2f4867015b412b15b76"))]) (hydra . [(20220910 1206) ((cl-lib (0 5)) (lv (0))) "Make bindings that stick around." tar ((:commit . "317e1de33086637579a7aeb60f77ed0405bf359b") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainers ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "bindings") (:url . "https://github.com/abo-abo/hydra"))]) - (hyperbole . [(20240808 941) ((emacs (27 1))) "GNU Hyperbole: The Everyday Hypertextual Information Manager" tar ((:commit . "ce1b5a3a343b4c168bbcb66b0ffcb20cc056eb52") (: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 . [(20240809 2154) ((emacs (28 1)) (map (3 0)) (compat (30 0 0 0)) (org (9 7 6)) (plz (0 9 0)) (persist (0 6 1)) (taxy-magit-section (0 13)) (transient (0 7 2))) "P2P filesystem" tar ((:commit . "d98780cfab3813e2a847d5c204fa725807062593") (:authors ("Joseph Turner" . "joseph@ushin.org")) (:maintainers ("Joseph Turner" . "~ushin/ushin@lists.sr.ht")) (:maintainer "Joseph Turner" . "~ushin/ushin@lists.sr.ht") (:url . "https://git.sr.ht/~ushin/hyperdrive.el"))]) + (hyperbole . [(20240814 558) ((emacs (27 1))) "GNU Hyperbole: The Everyday Hypertextual Information Manager" tar ((:commit . "690126165df951912291c89fccf847756cda7620") (: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 . [(20240812 1909) ((emacs (28 1)) (map (3 0)) (compat (30 0 0 0)) (org (9 7 6)) (plz (0 9 0)) (persist (0 6 1)) (taxy-magit-section (0 13)) (transient (0 7 4))) "P2P filesystem" tar ((:commit . "468cda16f0529e05a60570af18d5e1a9ef877cd5") (: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") (:keywords "outlines") (:url . "https://github.com/vifon/hyperlist-mode"))]) (hyperspace . [(20230518 442) ((emacs (25)) (s (1 12 0))) "Get there from here" tar ((:commit . "f574d07fd8715e806ba4f0487b73c699963baed3") (:authors ("Ian Eure" . "ian@retrospec.tv")) (:maintainers ("Ian Eure" . "ian@retrospec.tv")) (:maintainer "Ian Eure" . "ian@retrospec.tv") (:keywords "tools" "convenience") (:url . "https://github.com/ieure/hyperspace-el"))]) @@ -2884,7 +2884,7 @@ (julia-mode . [(20240506 1205) ((emacs (26 1))) "Major mode for editing Julia source code" tar ((:commit . "d360ad5285b8a0be1818fd6c2b4307c34e468c6e") (:keywords "languages") (:url . "https://github.com/JuliaEditorSupport/julia-emacs"))]) (julia-repl . [(20240408 850) ((emacs (25 1)) (s (1 12))) "A minor mode for a Julia REPL" tar ((:commit . "801d0fc3d8f6f08f66a11515e6517739a0b312a1") (:authors ("Tamas Papp" . "tkpapp@gmail.com")) (:maintainers ("Tamas Papp" . "tkpapp@gmail.com")) (:maintainer "Tamas Papp" . "tkpapp@gmail.com") (:keywords "languages") (:url . "https://github.com/tpapp/julia-repl"))]) (julia-shell . [(20161125 1910) ((julia-mode (0 3))) "Major mode for an inferior Julia shell" tar ((:commit . "583a0b2ca20461ab4356929fd0f2212c22341b69") (:authors ("Dennis Ogbe" . "dogbe@purdue.edu")) (:maintainers ("Dennis Ogbe" . "dogbe@purdue.edu")) (:maintainer "Dennis Ogbe" . "dogbe@purdue.edu"))]) - (julia-snail . [(20240521 22) ((emacs (26 2)) (dash (2 16 0)) (julia-mode (0 3)) (s (1 12 0)) (spinner (1 7 3)) (popup (0 5 9))) "Julia Snail" tar ((:commit . "f7784c50078332aeeb8e388bf5b8f13042b7406b") (:url . "https://github.com/gcv/julia-snail"))]) + (julia-snail . [(20240812 840) ((emacs (26 2)) (dash (2 16 0)) (julia-mode (0 3)) (s (1 12 0)) (spinner (1 7 3)) (popup (0 5 9))) "Julia Snail" tar ((:commit . "dff92c4250e40a6cc106f0ea993f9631ad55eb7c") (:url . "https://github.com/gcv/julia-snail"))]) (julia-ts-mode . [(20230921 1433) ((emacs (29 1)) (julia-mode (0 4))) "Major mode for Julia source code using tree-sitter" tar ((:commit . "44260b265359c7ed4052398e099ad019ce899109") (:keywords "julia" "languages" "tree-sitter") (:url . "https://github.com/ronisbr/julia-ts-mode"))]) (julia-vterm . [(20240514 724) ((emacs (25 1)) (vterm (0 0 1))) "A mode for Julia REPL using vterm" tar ((:commit . "2298cd42d354f069adbb7bb06b3b15222e5f54a2") (:keywords "languages" "julia") (:url . "https://github.com/shg/julia-vterm.el"))]) (jumblr . [(20170727 2043) ((s (1 8 0)) (dash (2 2 0))) "an anagram game for emacs" tar ((:commit . "34533dfb9db8538c005f4eaffafeff7ed193729f") (:keywords "anagram" "word game" "games") (:url . "https://github.com/mkmcc/jumblr"))]) @@ -2902,7 +2902,7 @@ (kaesar-file . [(20230614 332) ((emacs (24 3)) (kaesar (0 1 1))) "AES encrypt/decrypt file" tar ((:commit . "be615884cbbb9838c5e6655abf7f112a8df03a06") (:authors ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainers ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainer "Masahiro Hayashi" . "mhayashi1120@gmail.com") (:keywords "data" "files") (:url . "https://github.com/mhayashi1120/Emacs-kaesar"))]) (kaesar-mode . [(20230626 401) ((emacs (24 3)) (kaesar (0 1 4))) "AES encrypt/decrypt buffer" tar ((:commit . "fd833c69ad3ced4a890eb162f4399d79a8ec199c") (:authors ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainers ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainer "Masahiro Hayashi" . "mhayashi1120@gmail.com") (:keywords "data" "convenience") (:url . "https://github.com/mhayashi1120/Emacs-kaesar"))]) (kaesar-pbkdf2 . [(20230626 2314) ((emacs (25 1))) "PBKDF2 extension for kaesar.el" tar ((:commit . "740eaea4d2510b78d30cceabf4be2c3daca66cf7") (:authors ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainers ("Masahiro Hayashi" . "mhayashi1120@gmail.com")) (:maintainer "Masahiro Hayashi" . "mhayashi1120@gmail.com") (:keywords "data") (:url . "https://github.com/mhayashi1120/Emacs-kaesar"))]) - (kagi . [(20240802 2153) ((emacs (29 1)) (markdown-mode (2 6)) (shell-maker (0 46 1))) "Kagi API integration" tar ((:commit . "9e48fc96d513f863acde3689077197306e3d205c") (:authors ("Bram Schoenmakers" . "me@bramschoenmakers.nl")) (:maintainers ("Bram Schoenmakers" . "me@bramschoenmakers.nl")) (:maintainer "Bram Schoenmakers" . "me@bramschoenmakers.nl") (:keywords "terminals" "wp") (:url . "https://codeberg.org/bram85/kagi.el"))]) + (kagi . [(20240811 2130) ((emacs (29 1)) (markdown-mode (2 6)) (shell-maker (0 46 1))) "Kagi API integration" tar ((:commit . "013749218495e2c1bf2bb203c6b61976963817b5") (:authors ("Bram Schoenmakers" . "me@bramschoenmakers.nl")) (:maintainers ("Bram Schoenmakers" . "me@bramschoenmakers.nl")) (:maintainer "Bram Schoenmakers" . "me@bramschoenmakers.nl") (:keywords "terminals" "wp") (:url . "https://codeberg.org/bram85/kagi.el"))]) (kakapo-mode . [(20171004 451) ((cl-lib (0 5))) "TABS (hard or soft) for indentation (leading whitespace), and SPACES for alignment." tar ((:commit . "67d516138172fd60782df94454b3d0bd247e84f3") (:keywords "indentation") (:url . "https://github.com/listx/kakapo-mode"))]) (kakoune . [(20230206 2037) ((ryo-modal (0 45)) (multiple-cursors (1 4)) (expand-region (0 11 0)) (emacs (25 1))) "A simulation, but not emulation, of kakoune" tar ((:commit . "b39c5605e896c55ea246f755c46171bd6d0768a8") (:authors ("Joseph Morag" . "jm4157@columbia.edu")) (:maintainers ("Joseph Morag" . "jm4157@columbia.edu")) (:maintainer "Joseph Morag" . "jm4157@columbia.edu") (:url . "https://github.com/jmorag/kakoune.el"))]) (kaleidoscope . [(20170808 817) ((s (1 11 0))) "Controlling Kaleidoscope-powered devices." tar ((:commit . "b89a243f6024099192f1bc38d8a54e3e7a654090") (:url . "https://github.com/algernon/kaleidoscope.el"))]) @@ -2946,7 +2946,7 @@ (kfg . [(20140909 538) ((f (0 17 1))) "an emacs configuration system" tar ((:commit . "ffc35b77f227d4c64a1271ec30d31333ffeb0013") (:authors ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainers ("Austin Bingham" . "austin.bingham@gmail.com")) (:maintainer "Austin Bingham" . "austin.bingham@gmail.com") (:url . "https://github.com/abingham/kfg"))]) (khalel . [(20240527 527) ((emacs (27 1))) "Import, edit and create calendar events through khal" tar ((:commit . "14ef50352394cd1d62b80bc17ab14f4f801f47cd") (:authors ("Hanno Perrey" . "http://gitlab.com/hperrey")) (:maintainers ("Hanno Perrey" . "hanno@hoowl.se")) (:maintainer "Hanno Perrey" . "hanno@hoowl.se") (:keywords "event" "calendar" "ics" "khal") (:url . "https://gitlab.com/hperrey/khalel"))]) (khardel . [(20231126 1502) ((emacs (27 1)) (yaml-mode (0 0 13))) "Integrate with khard" tar ((:commit . "205e374b36252183a146a7a8f857bcf95a77edc3") (:authors ("Damien Cassou" . "damien@cassou.me")) (:maintainers ("Damien Cassou" . "damien@cassou.me")) (:maintainer "Damien Cassou" . "damien@cassou.me") (:url . "https://github.com/DamienCassou/khardel"))]) - (khoj . [(20240809 1241) ((emacs (27 1)) (transient (0 3 0)) (dash (2 19 1))) "AI copilot for your Second Brain" tar ((:commit . "acf1c14122fde4a79dbaf77dd1ff63c29422f43c") (:authors ("Debanjum Singh Solanky" . "debanjum@khoj.dev") ("Saba Imran" . "saba@khoj.dev")) (:maintainers ("Debanjum Singh Solanky" . "debanjum@khoj.dev") ("Saba Imran" . "saba@khoj.dev")) (:maintainer "Debanjum Singh Solanky" . "debanjum@khoj.dev") (:keywords "search" "chat" "ai" "org-mode" "outlines" "markdown" "pdf" "image") (:url . "https://github.com/khoj-ai/khoj/tree/master/src/interface/emacs"))]) + (khoj . [(20240811 1030) ((emacs (27 1)) (transient (0 3 0)) (dash (2 19 1))) "Your Second Brain" tar ((:commit . "7815e02dd421b705461df6a97033dafd61ba6419") (:authors ("Debanjum Singh Solanky" . "debanjum@khoj.dev") ("Saba Imran" . "saba@khoj.dev")) (:maintainers ("Debanjum Singh Solanky" . "debanjum@khoj.dev") ("Saba Imran" . "saba@khoj.dev")) (:maintainer "Debanjum Singh Solanky" . "debanjum@khoj.dev") (:keywords "search" "chat" "ai" "org-mode" "outlines" "markdown" "pdf" "image") (:url . "https://github.com/khoj-ai/khoj/tree/master/src/interface/emacs"))]) (kibit-helper . [(20150508 1533) ((s (0 8)) (emacs (24))) "Conveniently use the Kibit Leiningen plugin from Emacs" tar ((:commit . "ec5f154db3bb0c838e86f527353f08644cede926") (:authors ("James Elliott" . "james@brunchboy.com")) (:maintainers ("James Elliott" . "james@brunchboy.com")) (:maintainer "James Elliott" . "james@brunchboy.com") (:keywords "languages" "clojure" "kibit") (:url . "http://www.github.com/brunchboy/kibit-helper"))]) (kill-file-path . [(20230306 1041) ((emacs (26))) "Copy file name into kill ring" tar ((:commit . "5dcbce69cbae17665216a32dd20f27de54c62972") (:authors ("Adam Chyła" . "adam@chyla.org")) (:maintainers ("Adam Chyła" . "adam@chyla.org")) (:maintainer "Adam Chyła" . "adam@chyla.org") (:keywords "files") (:url . "https://github.com/chyla/kill-file-path/kill-file-path.el"))]) (kill-or-bury-alive . [(20230606 1503) ((emacs (24 4))) "Precise control over buffer killing" tar ((:commit . "16c393db6ad0c7e184af0a24d26b637e23543b1f") (:authors ("Mark Karpov" . "markkarpov92@gmail.com")) (:maintainers ("Mark Karpov" . "markkarpov92@gmail.com")) (:maintainer "Mark Karpov" . "markkarpov92@gmail.com") (:keywords "convenience") (:url . "https://github.com/mrkkrp/kill-or-bury-alive"))]) @@ -3112,7 +3112,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") (: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 . [(20240722 2332) nil "Compact syntax for short lambda" tar ((:commit . "ab2b4b1f4eb1abe9dbac99d0d6df45108aaef7cd") (:keywords "extensions") (:url . "https://git.sr.ht/~tarsius/llama"))]) + (llama . [(20240811 23) (("emacs" (26 1))) "Compact syntax for short lambda" tar ((:commit . "d98debc6ca4c7cc86e9dd944b2393698802ef077") (:keywords "extensions") (:url . "https://github.com/tarsius/llama"))]) (llama-cpp . [(20240511 1039) ((emacs (27 1)) (dash (2 19 1))) "A client for llama-cpp server" tar ((:commit . "5cea3698aa63921b21888f126cae4f3ebc1baa39") (:authors ("Evgeny Kurnevsky" . "kurnevsky@gmail.com")) (:maintainers ("Evgeny Kurnevsky" . "kurnevsky@gmail.com")) (:maintainer "Evgeny Kurnevsky" . "kurnevsky@gmail.com") (:keywords "tools") (:url . "https://github.com/kurnevsky/llama.el"))]) (llvm-ts-mode . [(20231120 1251) ((emacs (29 1))) "LLVM major mode using tree-sitter" tar ((:commit . "9974601dcddbeffc4ad47598d63d3c1a83bb6fb9") (:authors ("Noah Peart" . "noah.v.peart@gmail.com")) (:maintainers ("Noah Peart" . "noah.v.peart@gmail.com")) (:maintainer "Noah Peart" . "noah.v.peart@gmail.com") (:keywords "languages" "tree-sitter" "llvm") (:url . "https://github.com/nverno/llvm-ts-mode"))]) (lms . [(20210820 2200) ((emacs (25 1))) "Squeezebox / Logitech Media Server frontend" tar ((:commit . "29593b4c18a570dfb2e60b196f24d407a1277daa") (:authors ("Iñigo Serna" . "inigoserna@gmx.com")) (:maintainers ("Iñigo Serna" . "inigoserna@gmx.com")) (:maintainer "Iñigo Serna" . "inigoserna@gmx.com") (:keywords "multimedia") (:url . "https://hg.serna.eu/emacs/lms"))]) @@ -3192,7 +3192,7 @@ (m-buffer . [(20240302 2255) ((seq (2 14))) "List-Oriented, Functional Buffer Manipulation" tar ((:commit . "8a51de3366599e7fa52e37b596c9ce226b6f04c5") (:authors ("Phillip Lord" . "phillip.lord@russet.org.uk")) (:maintainers ("Phillip Lord" . "phillip.lord@russet.rg.uk")) (:maintainer "Phillip Lord" . "phillip.lord@russet.rg.uk"))]) (mac-pseudo-daemon . [(20211208 138) ((cl-lib (0 1))) "Daemon mode that plays nice with Mac OS." tar ((:commit . "462031a53255185ae25eb10ae1f4272e49ad70f7") (:keywords "convenience" "osx" "mac") (:url . "https://github.com/DarwinAwardWinner/mac-pseudo-daemon"))]) (maces-game . [(20170903 1551) ((dash (2 12 0)) (cl-lib (0 5)) (emacs (24))) "another anagram game." tar ((:commit . "6a067422d305ac51612842930ed6686dc615ffec") (:authors ("Pawel Bokota" . "pawelb.lnx@gmail.com")) (:maintainers ("Pawel Bokota" . "pawelb.lnx@gmail.com")) (:maintainer "Pawel Bokota" . "pawelb.lnx@gmail.com") (:keywords "games" "word games" "anagram") (:url . "https://github.com/pawelbx/anagram-game"))]) - (macports . [(20240809 105) ((emacs (26 1)) (transient (0 1 0))) "A porcelain for MacPorts" tar ((:commit . "0eba182bd6e7a8e711f790a8baddc43790bd7d3c") (:keywords "convenience") (:url . "https://github.com/amake/macports.el"))]) + (macports . [(20240814 1020) ((emacs (26 1)) (transient (0 1 0))) "A porcelain for MacPorts" tar ((:commit . "594b0dcd596464270d5e954d7c78015df2132c33") (:keywords "convenience") (:url . "https://github.com/amake/macports.el"))]) (macro-math . [(20130328 1604) nil "in-buffer mathematical operations" tar ((:commit . "216e59371e9ee39c34117ba79b9acd78bb415750") (:authors ("Nikolaj Schumacher" . "bugs*nschumde")) (:maintainers ("Nikolaj Schumacher" . "bugs*nschumde")) (:maintainer "Nikolaj Schumacher" . "bugs*nschumde") (:keywords "convenience") (:url . "http://nschum.de/src/emacs/macro-math/"))]) (macrostep . [(20240513 2203) ((cl-lib (0 5)) (compat (29))) "Interactive macro expander" tar ((:commit . "4939d88779761e8b5461b4cf73f86600172987db") (:authors ("Jon Oddie" . "j.j.oddie@gmail.com")) (:maintainers ("Jeremy Bryant" . "jb@jeremybryant.net")) (:maintainer "Jeremy Bryant" . "jb@jeremybryant.net") (:keywords "lisp" "languages" "macro" "debugging") (:url . "https://github.com/emacsorphanage/macrostep"))]) (macrostep-geiser . [(20210717 801) ((emacs (24 4)) (macrostep (0 9)) (geiser (0 12))) "Macrostep for `geiser'" tar ((:commit . "f6a2d5bb96ade4f23df557649af87ebd0cc45125") (:keywords "languages" "scheme") (:url . "https://github.com/nbfalcon/macrostep-geiser"))]) @@ -3200,9 +3200,9 @@ (mag-menu . [(20150505 1850) ((splitter (0 1 0))) "Intuitive keyboard-centric menu system" tar ((:commit . "9b9277021cd09fb1dba64b1d2a00705d20914bd6") (:keywords "convenience") (:url . "https://github.com/chumpage/mag-menu"))]) (magic-filetype . [(20240130 1805) ((emacs (24 3)) (s (1 9 0))) "Enhance filetype major mode" tar ((:commit . "3979ddbd8066d7390e31bde2b35f997c5f5f4516") (:authors ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainers ("USAMI Kenta" . "tadsan@zonu.me")) (:maintainer "USAMI Kenta" . "tadsan@zonu.me") (:keywords "emulations" "vim" "ft" "file" "magic-mode") (:url . "https://github.com/emacs-php/magic-filetype.el"))]) (magic-latex-buffer . [(20210306 422) ((cl-lib (0 5)) (emacs (25 1))) "Magically enhance LaTeX-mode font-locking for semi-WYSIWYG editing" tar ((:commit . "903ec91872760e47c0e5715795f8465173615098") (:url . "http://zk-phi.github.io/"))]) - (magik-mode . [(20240521 1419) ((emacs (24 4)) (compat (28 1))) "Emacs major mode for Smallworld Magik files" tar ((:commit . "51ec2d21e4d68fc549d2022f86a882e090541ec3") (:keywords "languages") (:url . "https://github.com/roadrunner1776/magik"))]) - (magit . [(20240808 1852) ((emacs (26 1)) (compat (30 0 0 0)) (dash (2 19 1)) (git-commit (4 0 0)) (magit-section (4 0 0)) (seq (2 24)) (transient (0 7 4)) (with-editor (3 4 1))) "A Git porcelain inside Emacs." tar ((:commit . "020aca7c9c4154dbc4a72acbd56165ecccea1bf1") (:authors ("Marius Vollmer" . "marius.vollmer@gmail.com") ("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/magit"))]) - (magit-annex . [(20231210 2140) ((cl-lib (0 3)) (magit (3 0 0))) "Control git-annex from Magit" tar ((:commit . "056f0d4462cdccbd7bb7589994da7fef9de766da") (:authors ("Kyle Meyer" . "kyle@kyleam.com") ("Rémi Vanicat" . "vanicat@debian.org")) (:maintainers ("Kyle Meyer" . "kyle@kyleam.com") ("Rémi Vanicat" . "vanicat@debian.org")) (:maintainer "Kyle Meyer" . "kyle@kyleam.com") (:keywords "vc" "tools") (:url . "https://github.com/magit/magit-annex"))]) + (magik-mode . [(20240814 914) ((emacs (24 4)) (compat (28 1))) "Emacs major mode for Smallworld Magik files" tar ((:commit . "e4b0ab1e97095973e7c40fed20e3acbfa1172823") (:keywords "languages") (:url . "https://github.com/roadrunner1776/magik"))]) + (magit . [(20240811 1419) ((emacs (26 1)) (compat (30 0 0 0)) (dash (20240510)) (git-commit (20240808)) (magit-section (20240808)) (seq (2 24)) (transient (20240805)) (with-editor (20240806))) "A Git porcelain inside Emacs." tar ((:commit . "a2739d7db1fdf19b95f36f6ddd15b0c1f523bd26") (:authors ("Marius Vollmer" . "marius.vollmer@gmail.com") ("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") (:keywords "git" "tools" "vc") (:url . "https://github.com/magit/magit"))]) + (magit-annex . [(20240811 1850) ((emacs (26 1)) (magit (4 0 0))) "Control git-annex from Magit" tar ((:commit . "9db0bc61461f222106c7ae3d8cd6d3de1f1b143f") (:authors ("Kyle Meyer" . "kyle@kyleam.com") ("Rémi Vanicat" . "vanicat@debian.org")) (:maintainers ("Kyle Meyer" . "kyle@kyleam.com") ("Rémi Vanicat" . "vanicat@debian.org")) (:maintainer "Kyle Meyer" . "kyle@kyleam.com") (:keywords "vc" "tools") (:url . "https://github.com/magit/magit-annex"))]) (magit-commit-mark . [(20240421 931) ((emacs (29 1)) (magit (3 3 0))) "Support marking commits as read" tar ((:commit . "d09d0df6f8a697446e9fac77428b32997b94c59e") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainers ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-magit-commit-mark"))]) (magit-delta . [(20220125 50) ((emacs (25 1)) (magit (20200426)) (xterm-color (2 0))) "Use Delta when displaying diffs in Magit" tar ((:commit . "5fc7dbddcfacfe46d3fd876172ad02a9ab6ac616") (:authors ("Dan Davison" . "dandavison7@gmail.com")) (:maintainers ("Dan Davison" . "dandavison7@gmail.com")) (:maintainer "Dan Davison" . "dandavison7@gmail.com") (:url . "https://github.com/dandavison/magit-delta"))]) (magit-diff-flycheck . [(20190524 551) ((magit (2)) (flycheck (31)) (seq (2)) (emacs (25 1))) "Report errors in diffs" tar ((:commit . "ad58efa312d708f25661dfcc2a7f83a833cca328") (:authors ("Alex Ragone" . "ragonedk@gmail.com")) (:maintainers ("Alex Ragone" . "ragonedk@gmail.com")) (:maintainer "Alex Ragone" . "ragonedk@gmail.com") (:keywords "convenience" "matching") (:url . "https://github.com/ragone/magit-diff-flycheck"))]) @@ -3214,7 +3214,7 @@ (magit-gitflow . [(20170929 824) ((magit (2 1 0)) (magit-popup (2 2 0))) "gitflow extension for magit" tar ((:commit . "cc41b561ec6eea947fe9a176349fb4f771ed865b") (:authors ("Jan Tatarik" . "Jan.Tatarik@gmail.com")) (:maintainers ("Jan Tatarik" . "Jan.Tatarik@gmail.com")) (:maintainer "Jan Tatarik" . "Jan.Tatarik@gmail.com") (:keywords "vc" "tools") (:url . "https://github.com/jtatarik/magit-gitflow"))]) (magit-gitlab . [(20240707 1506) ((emacs (26 1)) (magit (3 3 0)) (ghub (3 6 0)) (transient (0 6 0))) "Magit plugin for manipulating GitLab merge requests" tar ((:commit . "6f10468f9091d02aa6f1ce4af914443209a7d2a5") (:authors ("Arvid Jakobsson" . "arvid.jakobsson@gmail.com")) (:maintainers ("Arvid Jakobsson" . "arvid.jakobsson@gmail.com")) (:maintainer "Arvid Jakobsson" . "arvid.jakobsson@gmail.com") (:url . "https://gitlab.com/arvidnl/magit-gitlab"))]) (magit-gptcommit . [(20240625 356) ((emacs (29 1)) (dash (2 13 0)) (magit (2 90 1)) (llm (0 16 1))) "Git commit with help of gpt" tar ((:commit . "91b23fde4a880566a4e493240865e3582cad7306") (:authors ("Tiou Lims" . "dourokinga@gmail.com")) (:maintainers ("Tiou Lims" . "dourokinga@gmail.com")) (:maintainer "Tiou Lims" . "dourokinga@gmail.com") (:url . "https://github.com/douo/magit-gptcommit"))]) - (magit-imerge . [(20230522 1054) ((emacs (25 1)) (magit (3 0 0))) "Magit extension for git-imerge" tar ((:commit . "b7cfe49a197c2cf5948109921e053711b156389d") (:authors ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainers ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainer "Kyle Meyer" . "kyle@kyleam.com") (:keywords "vc" "tools") (:url . "https://github.com/magit/magit-imerge"))]) + (magit-imerge . [(20240811 1933) ((emacs (26 1)) (magit (4 0 0))) "Magit extension for git-imerge" tar ((:commit . "e9955c3b4dac2661f67d9882ed3367471e529cfc") (:authors ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainers ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainer "Kyle Meyer" . "kyle@kyleam.com") (:keywords "vc" "tools") (:url . "https://github.com/magit/magit-imerge"))]) (magit-lfs . [(20221031 1447) ((emacs (24 4)) (magit (2 10 3)) (dash (2 13 0))) "Magit plugin for Git LFS" tar ((:commit . "cd9f46e1840270be27e2c2d9dcf036ff0781f66d") (:authors ("Junyoung/Clare Jang" . "jjc9310@gmail.com")) (:maintainers ("Junyoung/Clare Jang" . "jjc9310@gmail.com")) (:maintainer "Junyoung/Clare Jang" . "jjc9310@gmail.com") (:keywords "magit" "git" "lfs" "tools" "vc") (:url . "https://github.com/ailrun/magit-lfs"))]) (magit-org-todos . [(20180709 1950) ((magit (2 0 0)) (emacs (24))) "Add local todo items to the magit status buffer" tar ((:commit . "9ffa3efb098434d837cab4bacd1601fdfc6fe999") (:keywords "org-mode" "magit" "tools") (:url . "http://github.com/danielma/magit-org-todos"))]) (magit-p4 . [(20220822 2022) ((emacs (25 1)) (magit (2 1)) (magit-popup (2 1)) (p4 (12 0)) (cl-lib (0 5))) "git-p4 plug-in for Magit" tar ((:commit . "0fd0f882eb14510714393c15c2ccb8d2c259f01e") (:authors ("Damian T. Dobroczyński" . "qoocku@gmail.com")) (:maintainers ("Aleksey Fedotov" . "lexa@cfotr.com")) (:maintainer "Aleksey Fedotov" . "lexa@cfotr.com") (:keywords "vc" "tools") (:url . "https://github.com/qoocku/magit-p4"))]) @@ -3222,11 +3222,11 @@ (magit-popup . [(20200719 1015) ((emacs (24 4)) (dash (2 13 0))) "Define prefix-infix-suffix command combos" tar ((:commit . "d8585fa39f88956963d877b921322530257ba9f5") (:authors ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainers ("Jonas Bernoulli" . "jonas@bernoul.li")) (:maintainer "Jonas Bernoulli" . "jonas@bernoul.li") (:keywords "bindings") (:url . "https://github.com/magit/magit-popup"))]) (magit-rbr . [(20181009 2016) ((magit (2 13 0)) (emacs (24 3))) "Support for git rbr in Magit" tar ((:commit . "029203b3e48537205052a058e964f058cd802c3c") (:authors ("Anatoly Fayngelerin" . "fanatoly+magitrbr@gmail.com")) (:maintainers ("Anatoly Fayngelerin" . "fanatoly+magitrbr@gmail.com")) (:maintainer "Anatoly Fayngelerin" . "fanatoly+magitrbr@gmail.com") (:keywords "git" "magit" "rbr" "tools") (:url . "https://github.com/fanatoly/magit-rbr"))]) (magit-reviewboard . [(20200727 1748) ((emacs (25 2)) (magit (2 13 0)) (s (1 12 0)) (request (0 3 0))) "Show open Reviewboard reviews in Magit" tar ((:commit . "aceedff88921f1dfef8a6b2fb18fe316fb7223a8") (:authors ("Jules Tamagnan" . "jtamagnan@gmail.com")) (:maintainers ("Jules Tamagnan" . "jtamagnan@gmail.com")) (:maintainer "Jules Tamagnan" . "jtamagnan@gmail.com") (:keywords "magit" "vc") (:url . "http://github.com/jtamagnan/magit-reviewboard"))]) - (magit-section . [(20240808 1852) ((emacs (26 1)) (compat (30 0 0 0)) (dash (2 19 1))) "Sections for read-only buffers." tar ((:commit . "020aca7c9c4154dbc4a72acbd56165ecccea1bf1") (:authors ("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") (:keywords "tools") (:url . "https://github.com/magit/magit"))]) + (magit-section . [(20240811 1419) ((emacs (26 1)) (compat (30 0 0 0)) (dash (20240510))) "Sections for read-only buffers." tar ((:commit . "a2739d7db1fdf19b95f36f6ddd15b0c1f523bd26") (:authors ("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") (:keywords "tools") (:url . "https://github.com/magit/magit"))]) (magit-stats . [(20230223 1819) ((emacs (25 1))) "Generates GIT Repo Statistics Report" tar ((:commit . "41b18e5fc664dba93981a7931f476632c5b54a7d") (:keywords "vc" "convenience") (:url . "https://github.com/LionyxML/magit-stats"))]) (magit-stgit . [(20231226 1514) ((emacs (24 4)) (magit (2 12 0)) (magit-popup (2 12 0))) "StGit extension for Magit" tar ((:commit . "59d1eb355caf4adbbdf1e351f5861de61b0b5efa") (:authors ("Lluís Vilanova" . "vilanova@ac.upc.edu")) (:maintainers ("Peter Grayson" . "pete@jpgrayson.net")) (:maintainer "Peter Grayson" . "pete@jpgrayson.net") (:keywords "git" "tools" "vc") (:url . "https://github.com/stacked-git/magit-stgit"))]) (magit-svn . [(20220314 1451) ((emacs (25 1)) (magit (2 90 1)) (transient (0 3 2))) "Git-Svn extension for Magit" tar ((:commit . "b8277081db90977247ae3900ea6afeb0ca644d36") (:authors ("Phil Jackson" . "phil@shellarchive.co.uk")) (:maintainers ("Phil Jackson" . "phil@shellarchive.co.uk")) (:maintainer "Phil Jackson" . "phil@shellarchive.co.uk") (:keywords "vc" "tools"))]) - (magit-tbdiff . [(20220527 2213) ((emacs (25 1)) (magit (3 0 0))) "Magit extension for range diffs" tar ((:commit . "55e6443710def357f3f78eea27d5bbe023d868f5") (:authors ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainers ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainer "Kyle Meyer" . "kyle@kyleam.com") (:keywords "vc" "tools") (:url . "https://github.com/magit/magit-tbdiff"))]) + (magit-tbdiff . [(20240811 1938) ((emacs (26 1)) (magit (4 0 0))) "Magit extension for range diffs" tar ((:commit . "03cb872f00dd7fc868de0c8d7661803b5d011aa0") (:authors ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainers ("Kyle Meyer" . "kyle@kyleam.com")) (:maintainer "Kyle Meyer" . "kyle@kyleam.com") (:keywords "vc" "tools") (:url . "https://github.com/magit/magit-tbdiff"))]) (magit-todos . [(20240519 247) ((emacs (26 1)) (async (1 9 2)) (dash (2 13 0)) (f (0 17 2)) (hl-todo (1 9 0)) (magit (2 13 0)) (pcre2el (1 8)) (s (1 12 0)) (transient (0 2 0))) "Show source file TODOs in Magit" tar ((:commit . "501c8db90ab59f8b619618b9d10db2a32a113727") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "magit" "vc") (:url . "http://github.com/alphapapa/magit-todos"))]) (magit-topgit . [(20161105 1623) ((emacs (24 4)) (magit (2 1 0))) "TopGit extension for Magit" tar ((:commit . "11489ea798bc88d0ea5244bbf725285eedfefbef") (:authors ("Yann Hodique" . "yann.hodique@gmail.com")) (:maintainers ("Robin Green" . "greenrd@greenrd.org")) (:maintainer "Robin Green" . "greenrd@greenrd.org") (:keywords "vc" "tools"))]) (magit-vcsh . [(20230402 1219) ((magit (2 90 1)) (vcsh (0 4)) (emacs (24 4))) "Magit vcsh integration" tar ((:commit . "fd6c86c066b14bbf78644d38eca9711d6d9544a1") (:authors ("těpán Němec" . "stepnem@smrk.net")) (:maintainers ("těpán Němec" . "stepnem@smrk.net")) (:maintainer "těpán Němec" . "stepnem@smrk.net") (:keywords "vc" "files" "magit") (:url . "http://git.smrk.net/magit-vcsh.el"))]) @@ -3255,7 +3255,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 . [(20240726 2129) ((emacs (27 1)) (compat (30))) "Enrich existing commands with completion annotations" tar ((:commit . "7a7f3363d042d1bf43ae697f4401638ed18230a5") (: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") (:keywords "docs" "help" "matching" "completion") (:url . "https://github.com/minad/marginalia"))]) + (marginalia . [(20240813 701) ((emacs (27 1)) (compat (30))) "Enrich existing commands with completion annotations" tar ((:commit . "c34fdacce64168cb20d710a87e66cc9d1f795a82") (: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") (: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") (: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"))]) @@ -3334,7 +3334,7 @@ (metronome . [(20230515 1850) ((emacs (25 1))) "The missing metronome for GNU Emacs" tar ((:commit . "4811b54d800d1bb69fd501ffeab3adf86978362d") (:authors ("Jonathan Gregory" . "jgrgatautisticidotorg")) (:maintainers ("Jonathan Gregory" . "jgrgatautisticidotorg")) (:maintainer "Jonathan Gregory" . "jgrgatautisticidotorg") (:url . "https://git.sr.ht/~jagrg/metronome"))]) (mew . [(20240515 152) nil "Messaging in the Emacs World" tar ((:commit . "6eedf619d726aa11f23962e406331b7aba8ac82c"))]) (mexican-holidays . [(20210604 1421) nil "Mexico holidays for Emacs calendar." tar ((:commit . "8e28907ea69f2c0ed9aad9f3b99664ca147379d0") (:authors ("Saúl Gutiérrez" . "me@sggc.me")) (:maintainers ("Saúl Gutiérrez" . "me@sggc.me")) (:maintainer "Saúl Gutiérrez" . "me@sggc.me") (:keywords "calendar") (:url . "https://github.com/sggutier/mexican-holidays"))]) - (meyvn . [(20240628 2350) ((emacs (25 1)) (cider (0 23)) (projectile (2 1)) (s (1 12)) (dash (2 17)) (parseedn (1 1 0)) (parseclj (1 1 0)) (geiser (0 12))) "Meyvn client" tar ((:commit . "f2b809da1d1bf66f1a215a0e5c64f95d10118b76") (:authors ("Daniel Szmulewicz" . "daniel.szmulewicz@gmail.com")) (:maintainers ("Daniel Szmulewicz" . "daniel.szmulewicz@gmail.com")) (:maintainer "Daniel Szmulewicz" . "daniel.szmulewicz@gmail.com") (:url . "https://github.com/danielsz/meyvn-el"))]) + (meyvn . [(20240813 2139) ((emacs (25 1)) (cider (0 23)) (projectile (2 1)) (s (1 12)) (dash (2 17)) (parseedn (1 1 0)) (parseclj (1 1 0)) (geiser (0 12))) "Meyvn client" tar ((:commit . "62802ab42ee021f89f980bd3de3e1336ad760944") (:authors ("Daniel Szmulewicz" . "daniel.szmulewicz@gmail.com")) (:maintainers ("Daniel Szmulewicz" . "daniel.szmulewicz@gmail.com")) (:maintainer "Daniel Szmulewicz" . "daniel.szmulewicz@gmail.com") (:url . "https://github.com/danielsz/meyvn-el"))]) (mgmtconfig-mode . [(20240330 2205) ((emacs (24 3))) "mgmt configuration management language" tar ((:commit . "1b00af6926d8699d9d04062f28fddd43c6340bac") (:authors ("Peter Oliver" . "mgmtconfig@mavit.org.uk")) (:maintainers ("Mgmt contributors" . "https://github.com/purpleidea/mgmt")) (:maintainer "Mgmt contributors" . "https://github.com/purpleidea/mgmt") (:keywords "languages") (:url . "https://github.com/purpleidea/mgmt/misc/emacs"))]) (mhc . [(20240419 10) ((calfw (20150703))) "Message Harmonized Calendaring system." tar ((:commit . "b527a88748651d06222ad24f7417941088515275") (:authors ("Yoshinari Nomura" . "nom@quickhack.net")) (:maintainers ("Yoshinari Nomura" . "nom@quickhack.net")) (:maintainer "Yoshinari Nomura" . "nom@quickhack.net") (:keywords "calendar") (:url . "http://www.quickhack.net/mhc"))]) (mic . [(20240806 1655) ((emacs (26 1))) "Minimal and combinable configuration manager" tar ((:commit . "f552ddf397e899e9c2b96ef4e56a08cc8804a1c5") (:authors ("ROCKTAKEY" . "rocktakey@gmail.com")) (:maintainers ("ROCKTAKEY" . "rocktakey@gmail.com")) (:maintainer "ROCKTAKEY" . "rocktakey@gmail.com") (:keywords "convenience") (:url . "https://github.com/ROCKTAKEY/mic"))]) @@ -3394,7 +3394,7 @@ (modern-sh . [(20211101 1001) ((emacs (25 1)) (hydra (0 15 0)) (eval-in-repl (0 9 7))) "Minor mode for editing shell script" tar ((:commit . "8ebebe77304aa8170f7af809e7564c79d3bd45da") (:keywords "languages" "programming") (:url . "https://github.com/damon-kwok/modern-sh"))]) (modtime-skip-mode . [(20140128 2201) nil "Minor mode for disabling modtime and supersession checks on files." tar ((:commit . "c0e49523aa26b2263a8693691ac775988015f592") (:authors ("Jordon Biondo" . "biondoj@mail.gvsu.edu")) (:maintainers ("Jordon Biondo" . "biondoj@mail.gvsu.edu")) (:maintainer "Jordon Biondo" . "biondoj@mail.gvsu.edu") (:url . "http://www.github.com/jordonbiondo/modtime-skip-mode"))]) (modular-config . [(20210726 1614) ((emacs (25 1))) "Organize your config into small and loadable modules" tar ((:commit . "043907d96efff70dfaea1e721de90bd35970e8bd") (:authors ("Sidharth Arya" . "sidhartharya10@gmail.com")) (:maintainers ("Sidharth Arya" . "sidhartharya10@gmail.com")) (:maintainer "Sidharth Arya" . "sidhartharya10@gmail.com") (:keywords "startup" "lisp" "tools") (:url . "https://github.com/SidharthArya/modular-config.el"))]) - (modus-themes . [(20240806 528) ((emacs (27 1))) "Elegant, highly legible and customizable themes" tar ((:commit . "243fc35181db62e4cadc10b29a8950072443eea0") (:authors ("Protesilaos Stavrou" . "info@protesilaos.com")) (:maintainers ("Protesilaos Stavrou" . "info@protesilaos.com")) (:maintainer "Protesilaos Stavrou" . "info@protesilaos.com") (:keywords "faces" "theme" "accessibility") (:url . "https://github.com/protesilaos/modus-themes"))]) + (modus-themes . [(20240811 502) ((emacs (27 1))) "Elegant, highly legible and customizable themes" tar ((:commit . "3002f6fb64e147ece4284880ffa93c6c3baa0e75") (:authors ("Protesilaos Stavrou" . "info@protesilaos.com")) (:maintainers ("Protesilaos Stavrou" . "info@protesilaos.com")) (:maintainer "Protesilaos Stavrou" . "info@protesilaos.com") (:keywords "faces" "theme" "accessibility") (:url . "https://github.com/protesilaos/modus-themes"))]) (moe-theme . [(20240716 854) nil "A colorful eye-candy theme. Moe, moe, kyun!" tar ((:commit . "4b3642157bfe9a9268310d321cfe67c8c236b5e1") (:authors ("kuanyui" . "azazabc123@gmail.com")) (:maintainers ("kuanyui" . "azazabc123@gmail.com")) (:maintainer "kuanyui" . "azazabc123@gmail.com") (:keywords "themes") (:url . "https://github.com/kuanyui/moe-theme.el"))]) (molar-mass . [(20220922 1752) ((emacs (24 3))) "Calculates molar mass of a molecule" tar ((:commit . "c3b686c4b621b45fa4b17857b4934eb4487d74f5") (:keywords "convenience" "chemistry") (:url . "https://github.com/sergiruiztrepat/molar-mass.el"))]) (molecule . [(20180527 743) ((emacs (25 1))) "Simple wrapper for molecule" tar ((:commit . "2ef72b81d9aa24ea782b71a061a3abdad6cae162") (:authors ("drymer" . "drymer[AT]autistici.org")) (:maintainers ("drymer" . "drymer[AT]autistici.org")) (:maintainer "drymer" . "drymer[AT]autistici.org") (:keywords ":" "languages" "terminals") (:url . "https://git.daemons.it/drymer/molecule.el"))]) @@ -3579,7 +3579,7 @@ (nntwitter . [(20230705 1110) ((emacs (25 1)) (dash (20190401)) (anaphora (20180618)) (request (20190819))) "Gnus Backend For Twitter" tar ((:commit . "e27acca9beeb6645dd13545d42f6d4d97d59d82c") (:keywords "news") (:url . "https://github.com/dickmao/nntwitter"))]) (no-clown-fiesta-theme . [(20240725 2030) ((emacs (26 1)) (autothemer (0 2))) "Not-so-colorful-theme" tar ((:commit . "d97f521d4e29181af59412ac32de34ca487345d8") (:url . "https://codeberg.org/ranmaru22/no-clown-fiesta-theme.el"))]) (no-emoji . [(20180515 1837) ((emacs (24))) "Show :emoji-name: instead of emoji characters" tar ((:commit . "ebceeab50dbfe4d60235180a57633745dbc18c77") (:authors ("Peter" . "craven@gmx.net")) (:maintainers ("Peter" . "craven@gmx.net")) (:maintainer "Peter" . "craven@gmx.net") (:keywords "extensions") (:url . "https://github.com/ecraven/no-emoji"))]) - (no-littering . [(20240805 1428) ((emacs (26 1)) (compat (30 0 0 0))) "Help keeping ~/.config/emacs clean" tar ((:commit . "8658ff9a6f27512218fdbf7bba328917a27d5a47") (:authors ("Jonas Bernoulli" . "emacs.no-littering@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.no-littering@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.no-littering@jonas.bernoulli.dev") (:keywords "convenience") (:url . "https://github.com/emacscollective/no-littering"))]) + (no-littering . [(20240812 1522) ((emacs (26 1)) (compat (30 0 0 0))) "Help keeping ~/.config/emacs clean" tar ((:commit . "d80f442c15a1e81ce939891bbb4497a0559831b9") (:authors ("Jonas Bernoulli" . "emacs.no-littering@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.no-littering@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.no-littering@jonas.bernoulli.dev") (:keywords "convenience") (:url . "https://github.com/emacscollective/no-littering"))]) (no-spam . [(20190724 1854) ((emacs (25 1))) "Add repeat delays to commands" tar ((:commit . "860860e4a0d59bd15c8e092dc42f5f7f769a428e") (:authors ("Daniel Phan" . "daniel.phan36@gmail.com")) (:maintainers ("Daniel Phan" . "daniel.phan36@gmail.com")) (:maintainer "Daniel Phan" . "daniel.phan36@gmail.com") (:keywords "keyboard" "tools") (:url . "https://github.com/mamapanda/no-spam"))]) (noaa . [(20240317 2321) ((emacs (27 1)) (kv (0 0 19)) (request (0 2 0)) (s (1 12 0))) "Get NOAA weather data" tar ((:commit . "7d68b5a580c64123f3bbd75f795a891dfdeb1746") (:keywords "calendar") (:url . "https://codeberg.org/thomp/noaa"))]) (noccur . [(20191015 719) nil "Run multi-occur on project/dired files" tar ((:commit . "fa91647a305e89561d3dbe53da002fff49abe0bb") (:authors ("Nicolas Petton" . "petton.nicolas@gmail.com")) (:maintainers ("Nicolas Petton" . "petton.nicolas@gmail.com")) (:maintainer "Nicolas Petton" . "petton.nicolas@gmail.com") (:keywords "convenience"))]) @@ -3644,7 +3644,7 @@ (ob-blockdiag . [(20210412 1541) nil "org-babel functions for blockdiag evaluation" tar ((:commit . "e997644e81cc67a7092e6e9bb13c66f160491efb") (:keywords "tools" "convenience") (:url . "https://github.com/corpix/ob-blockdiag.el"))]) (ob-browser . [(20170720 1918) ((org (8))) "Render HTML in org-mode blocks." tar ((:commit . "a347d9df1c87b7eb660be8723982c7ad2563631a") (:authors ("Kris Jenkins" . "krisajenkins@gmail.com")) (:maintainers ("Kris Jenkins" . "krisajenkins@gmail.com")) (:maintainer "Kris Jenkins" . "krisajenkins@gmail.com") (:keywords "org" "babel" "browser" "phantomjs") (:url . "https://github.com/krisajenkins/ob-browser"))]) (ob-cfengine3 . [(20230226 1954) ((emacs (24 1))) "Org Babel functions for CFEngine 3" tar ((:commit . "52aa32fdfa412860837e795d17d50dac237e56e4") (:authors ("Nick Anderson" . "nick@cmdln.org")) (:maintainers ("Nick Anderson" . "nick@cmdln.org")) (:maintainer "Nick Anderson" . "nick@cmdln.org") (:keywords "tools" "convenience") (:url . "https://github.com/nickanderson/ob-cfengine3"))]) - (ob-chatgpt-shell . [(20240806 1414) ((emacs (27 1)) (chatgpt-shell (1 0 3))) "Org babel functions for ChatGPT evaluation" tar ((:commit . "e42e4d42b8978cdf8d7e7e8a5b7936cba80a035a") (:url . "https://github.com/xenodium/chatgpt-shell"))]) + (ob-chatgpt-shell . [(20240814 938) ((emacs (27 1)) (chatgpt-shell (1 0 3))) "Org babel functions for ChatGPT evaluation" tar ((:commit . "6fb85746bd0d316c97e375dcc14eb5314778e74d") (:url . "https://github.com/xenodium/chatgpt-shell"))]) (ob-clojurescript . [(20180406 1828) ((emacs (24 4)) (org (9 0))) "org-babel functions for ClojureScript evaluation" tar ((:commit . "17ee1558aa94c7b0246fd03f684884122806cfe7") (:keywords "literate programming" "reproducible research") (:url . "https://gitlab.com/statonjr/ob-clojurescript"))]) (ob-coffee . [(20170725 1424) ((org (8))) "org-babel functions for coffee-script evaluation" tar ((:commit . "7f0b330273e8af7777de87a75fe52a89798e4548") (:authors ("ZHOU Feng" . "zf.pascal@gmail.com")) (:maintainers ("ZHOU Feng" . "zf.pascal@gmail.com")) (:maintainer "ZHOU Feng" . "zf.pascal@gmail.com") (:keywords "org" "babel" "coffee-script") (:url . "http://github.com/zweifisch/ob-coffee"))]) (ob-coffeescript . [(20180126 719) ((emacs (24 4))) "org-babel functions for coffee-script evaluation, and fully implementation!" tar ((:commit . "5a5bb04aea9c2a6eab5b05f90f5c7cb6de7b4261") (:authors ("Brantou" . "brantou89@gmail.com")) (:maintainers ("Brantou" . "brantou89@gmail.com")) (:maintainer "Brantou" . "brantou89@gmail.com") (:keywords "coffee-script" "literate programming" "reproducible research") (:url . "https://github.com/brantou/ob-coffeescript"))]) @@ -3652,7 +3652,7 @@ (ob-crystal . [(20180126 718) ((emacs (24 3))) "org-babel functions for Crystal evaluation" tar ((:commit . "b3bb27a21a4cefef3f5aeef52718b694bd51245b") (:authors ("Brantou" . "brantou89@gmail.com")) (:maintainers ("Brantou" . "brantou89@gmail.com")) (:maintainer "Brantou" . "brantou89@gmail.com") (:keywords "crystal" "literate programming" "reproducible research") (:url . "https://github.com/brantou/ob-crystal"))]) (ob-cypher . [(20200521 936) ((s (1 9 0)) (cypher-mode (0 0 6)) (dash (2 10 0)) (dash-functional (1 2 0))) "query neo4j using cypher in org-mode blocks" tar ((:commit . "da9f97339474a48d759fc128cee610c0bc9ae6c0") (:authors ("ZHOU Feng" . "zf.pascal@gmail.com")) (:maintainers ("ZHOU Feng" . "zf.pascal@gmail.com")) (:maintainer "ZHOU Feng" . "zf.pascal@gmail.com") (:keywords "org" "babel" "cypher" "neo4j") (:url . "http://github.com/zweifisch/ob-cypher"))]) (ob-d2 . [(20230314 352) ((emacs (24 1))) "Org-babel functions for d2" tar ((:commit . "5d197f8225a9fb4da997235b231abe30049c6825") (:keywords "languages") (:url . "https://github.com/xcapaldi/ob-d2"))]) - (ob-dall-e-shell . [(20240806 1414) ((emacs (27 1)) (dall-e-shell (0 37 1))) "Org babel functions for DALL-E evaluation" tar ((:commit . "e42e4d42b8978cdf8d7e7e8a5b7936cba80a035a") (:url . "https://github.com/xenodium/chatgpt-shell"))]) + (ob-dall-e-shell . [(20240814 938) ((emacs (27 1)) (dall-e-shell (0 37 1))) "Org babel functions for DALL-E evaluation" tar ((:commit . "6fb85746bd0d316c97e375dcc14eb5314778e74d") (:url . "https://github.com/xenodium/chatgpt-shell"))]) (ob-dao . [(20170816 1558) ((org (8))) "Org Babel Functions for Dao" tar ((:commit . "8c62bd800b1f572860e30be4b72c71fa415a2e31") (:authors ("Chunyang Xu" . "mail@xuchunyang.me")) (:maintainers ("Chunyang Xu" . "mail@xuchunyang.me")) (:maintainer "Chunyang Xu" . "mail@xuchunyang.me") (:keywords "literate programming" "reproducible research" "org" "babel" "dao") (:url . "https://github.com/xuchunyang/ob-dao"))]) (ob-dart . [(20221201 633) ((emacs (24 4))) "Evaluate Dart source blocks in org-mode" tar ((:commit . "f6d5664d5cc8b15e002f6899f8adedcb10ced5f1") (:keywords "languages") (:url . "http://github.org/mzimmerm/ob-dart"))]) (ob-deno . [(20201019 101) ((emacs (26 1))) "Babel Functions for Javascript/TypeScript with Deno" tar ((:commit . "e3b06d7662687e402905b9de4ad1d5816e89b842") (:keywords "literate programming" "reproducible research" "javascript" "typescript" "tools") (:url . "https://github.com/taiju/ob-deno"))]) @@ -3991,7 +3991,7 @@ (orglue . [(20200411 311) ((org (9 3)) (epic (0 2))) "more functionality to org-mode." tar ((:commit . "9d5a8e24be9acb8c55bb4d6aa8b98e30e2677401") (:authors ("Yoshinari Nomura" . "nom@quickhack.net")) (:maintainers ("Yoshinari Nomura" . "nom@quickhack.net")) (:maintainer "Yoshinari Nomura" . "nom@quickhack.net") (:keywords "org"))]) (orgmdb . [(20231003 2144) ((emacs (27 1)) (dash (2 11 0)) (s (1 12 0)) (org (8 0 0))) "An OMDb API client with some convenience functions" tar ((:commit . "4338a0a34d500a214df8293590960011f761fe24") (:authors ("Isa Mert Gurbuz" . "isamert@protonmail.com")) (:maintainers ("Isa Mert Gurbuz" . "isamert@protonmail.com")) (:maintainer "Isa Mert Gurbuz" . "isamert@protonmail.com") (:url . "https://github.com/isamert/orgmdb"))]) (orgnav . [(20170608 1713) ((helm (2 7 0)) (s (1 11 0)) (dash (1 11 0)) (emacs (24))) "Org tree navigation using helm" tar ((:commit . "9e2cac9c1a67af5f0080e60022e821bf7b70312d") (:authors ("Facet Framer" . "(facet@facetframer.com)")) (:maintainers ("Facet Framer" . "(facet@facetframer.com)")) (:maintainer "Facet Framer" . "(facet@facetframer.com)") (:keywords "convenience" "outlines") (:url . "http://github.com/facetframer/orgnav"))]) - (orgnote . [(20240516 1842) ((emacs (27 1))) "Sync org-roam notes with OrgNote app" tar ((:commit . "0b6ab72bede2fa757758016ae69db2cc8332fa2d") (:authors ("Artur Yaroshenko" . "artawower@protonmail.com")) (:maintainers ("Artur Yaroshenko" . "artawower@protonmail.com")) (:maintainer "Artur Yaroshenko" . "artawower@protonmail.com") (:url . "https://github.com/Artawower/orgnote.el"))]) + (orgnote . [(20240813 2022) ((emacs (27 1))) "Sync org-roam notes with OrgNote app" tar ((:commit . "ed9dae520af7560ce4235c9ce4847f257e32cf4e") (:authors ("Artur Yaroshenko" . "artawower@protonmail.com")) (:maintainers ("Artur Yaroshenko" . "artawower@protonmail.com")) (:maintainer "Artur Yaroshenko" . "artawower@protonmail.com") (:url . "https://github.com/Artawower/orgnote.el"))]) (orgstrap . [(20230408 2232) ((emacs (24 4))) "Bootstrap an Org file using file local variables" tar ((:commit . "f35bccde556b0f82515e79ee69f4379469276356") (:keywords "lisp" "org" "org-mode" "bootstrap") (:url . "https://github.com/tgbugs/orgstrap"))]) (orgtbl-aggregate . [(20240616 506) ((emacs (26 1))) "Create an aggregated Org table from another one" tar ((:commit . "f343b6009d87630588d39dc3d92651008c1bad13") (:keywords "data" "extensions") (:url . "https://github.com/tbanel/orgaggregate/blob/master/README.org"))]) (orgtbl-ascii-plot . [(20230122 816) nil "ascii-art bar plots in org-mode tables" tar ((:commit . "4160128045b271bc1aef3d14dbf0c5b53ae58bd2") (:keywords "org" "table" "ascii" "plot"))]) @@ -4460,7 +4460,7 @@ (python-view-data . [(20230508 543) ((emacs (28 1)) (python (0 2)) (csv-mode (1 12))) "View data in python" tar ((:commit . "1dd5f99679db9767530cfc20642a40a48bd479be") (: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/python-view-data"))]) (python-x . [(20230117 1408) ((python (0 24)) (folding (0)) (cl-lib (0 5))) "python.el extras for interactive evaluation" tar ((:commit . "744924e7468200f3e8ac7ad60a496ad9d080308e") (:authors ("Yuri D'Elia" . "wavexx@thregr.org")) (:maintainer "Yuri D'Elia" . "wavexx@thregr.org"))]) (pythonic . [(20230821 1733) ((emacs (25 1)) (s (1 9)) (f (0 17 2))) "Utility functions for writing pythonic emacs package" tar ((:commit . "f6e0bec552319341f260a5c4740288799c2b3a5b") (:authors ("Artem Malyshev" . "proofit404@gmail.com")) (:maintainers ("Artem Malyshev" . "proofit404@gmail.com")) (:maintainer "Artem Malyshev" . "proofit404@gmail.com") (:keywords "convenience" "pythonic") (:url . "https://github.com/proofit404/pythonic"))]) - (pythontest . [(20240610 2226) ((emacs (29 1))) "Testing executor for python" tar ((:commit . "5b46552a7afdd91070ac528909e032d0df5aa2da") (:authors ("Erick Navarro" . "erick@navarro.io")) (:maintainers ("Erick Navarro" . "erick@navarro.io")) (:maintainer "Erick Navarro" . "erick@navarro.io") (:url . "https://github.com/erickgnavar/pythontest.el"))]) + (pythontest . [(20240813 1322) ((emacs (29 1))) "Testing executor for python" tar ((:commit . "4bb4f330c13ef82bb6e4a4b15c47cb3fede83523") (:authors ("Erick Navarro" . "erick@navarro.io")) (:maintainers ("Erick Navarro" . "erick@navarro.io")) (:maintainer "Erick Navarro" . "erick@navarro.io") (:url . "https://github.com/erickgnavar/pythontest.el"))]) (pyvenv . [(20211014 707) nil "Python virtual environment interface" tar ((:commit . "31ea715f2164dd611e7fc77b26390ef3ca93509b") (:authors ("Jorgen Schaefer" . "contact@jorgenschaefer.de")) (:maintainers ("Jorgen Schaefer" . "contact@jorgenschaefer.de")) (:maintainer "Jorgen Schaefer" . "contact@jorgenschaefer.de") (:keywords "python" "virtualenv" "tools") (:url . "http://github.com/jorgenschaefer/pyvenv"))]) (pyvenv-auto . [(20230106 415) ((emacs (26 3)) (pyvenv (1 21))) "Automatically switch Python venvs" tar ((:commit . "b4365e60e3ba747a5fec8ca909f64fe8c73d8db2") (:url . "https://github.com/nryotaro/pyvenv-auto"))]) (q-mode . [(20230412 53) ((emacs (24))) "A q editing mode" tar ((:commit . "d89b359d5a26234336487ab4e42eb5878ad3c5a5") (:keywords "faces" "files" "q") (:url . "https://github.com/psaris/q-mode"))]) @@ -4523,7 +4523,7 @@ (read-only-cfg . [(20210717 205) ((emacs (24 3))) "Make files read-only based on user config" tar ((:commit . "fa16d6018a5a29f26adf6007b6b76ea1b3c0bfce") (:authors ("pfchen" . "pfchen31@gmail.com")) (:maintainers ("pfchen" . "pfchen31@gmail.com")) (:maintainer "pfchen" . "pfchen31@gmail.com") (:keywords "tools" "convenience") (:url . "https://github.com/pfchen/read-only-cfg"))]) (readable-numbers . [(20220711 911) ((emacs (24 1))) "Visually separate long integers" tar ((:commit . "a3ebdcdd91d32f044b68541a00e162396e4acb38") (:authors ("Oscar Najera" . "https://oscarnajera.com")) (:maintainers ("Oscar Najera" . "hi@oscarnajera.com")) (:maintainer "Oscar Najera" . "hi@oscarnajera.com") (:url . "https://github.com/Titan-C/cardano.el"))]) (readline-complete . [(20150708 1437) nil "offers completions in shell mode" tar ((:commit . "30c020c37b2741160cc37e656e13c85d826a0ebf") (:authors ("Christopher Monsanto" . "chris@monsan.to")) (:maintainers ("Christopher Monsanto" . "chris@monsan.to")) (:maintainer "Christopher Monsanto" . "chris@monsan.to"))]) - (ready-player . [(20240809 945) ((emacs (28 1))) "Open media files in ready-player major mode" tar ((:commit . "4ecc4dc298bd5e91f835097620ac29134d86a469") (:url . "https://github.com/xenodium/ready-player"))]) + (ready-player . [(20240811 1854) ((emacs (28 1))) "Open media files in ready-player major mode" tar ((:commit . "a1fe64da99948ba79ce310552e0f1be64354969f") (:url . "https://github.com/xenodium/ready-player"))]) (real-auto-save . [(20200505 1537) ((emacs (24 4))) "Automatically save your buffers/files at regular intervals" tar ((:commit . "8e51241e5ba7b07b91d8188c14cf193017640292") (:authors ("Chaoji Li" . "lichaojiATgmailDOTcom") ("Anand Reddy Pandikunta" . "anand21nandaATgmailDOTcom")) (:maintainers ("Chaoji Li" . "lichaojiATgmailDOTcom") ("Anand Reddy Pandikunta" . "anand21nandaATgmailDOTcom")) (:maintainer "Chaoji Li" . "lichaojiATgmailDOTcom") (:url . "https://github.com/ChillarAnand/real-auto-save"))]) (realgud . [(20231113 1910) ((load-relative (1 3 1)) (loc-changes (1 2)) (test-simple (1 3 0)) (emacs (25))) "A modular front-end for interacting with external debuggers" tar ((:commit . "365063ea8ce8ec6a852cb388088d84147421c3c2") (:authors ("Rocky Bernstein" . "rocky@gnu.org")) (:maintainers ("Rocky Bernstein" . "rocky@gnu.org")) (:maintainer "Rocky Bernstein" . "rocky@gnu.org") (:keywords "debugger" "gdb" "python" "perl" "go" "bash" "zsh" "bashdb" "zshdb" "remake" "trepan" "perldb" "pdb") (:url . "https://github.com/realgud/realgud/"))]) (realgud-byebug . [(20190520 1140) ((realgud (1 4 5)) (load-relative (1 2)) (cl-lib (0 5)) (emacs (24))) "Realgud front-end to the Ruby byebug debugger" tar ((:commit . "f8f20b92c6b13f75cc9797921c0e28d3def48b1c") (:url . "http://github.com/rocky/realgud-byebug"))]) @@ -4681,7 +4681,7 @@ (rust-auto-use . [(20200608 1359) nil "Utility to automatically insert Rust use statements" tar ((:commit . "d5205f7b9b9eae0f7d0893f87d3391464719f9c0") (:authors ("Rotem Yaari" . "rotemy@MBP.local")) (:maintainers ("Rotem Yaari" . "rotemy@MBP.local")) (:maintainer "Rotem Yaari" . "rotemy@MBP.local") (:keywords "languages"))]) (rust-mode . [(20240520 749) ((emacs (25 1))) "A major-mode for editing Rust source code" tar ((:commit . "d00d83d3a207a5b7c2994392b2781f627e3159ce") (:authors ("Mozilla" . "rust-mode@noreply.github.com")) (:maintainers ("Mozilla" . "rust-mode@noreply.github.com")) (:maintainer "Mozilla" . "rust-mode@noreply.github.com") (:keywords "languages") (:url . "https://github.com/rust-lang/rust-mode"))]) (rust-playground . [(20200116 1043) ((emacs (24 3))) "Local Rust playground for short code snippets." tar ((:commit . "5a117781dcb66065bea7830dd73618008fc34949") (:authors ("Alexander I.Grafov + all the contributors" . "grafov@gmail.com")) (:maintainers ("Alexander I.Grafov + all the contributors" . "grafov@gmail.com")) (:maintainer "Alexander I.Grafov + all the contributors" . "grafov@gmail.com") (:keywords "tools" "rust") (:url . "https://github.com/grafov/rust-playground"))]) - (rustic . [(20240809 722) ((emacs (26 1)) (rust-mode (1 0 3)) (dash (2 13 0)) (f (0 18 2)) (let-alist (1 0 4)) (markdown-mode (2 3)) (project (0 3 0)) (s (1 10 0)) (spinner (1 7 3)) (xterm-color (1 6)) (flycheck (34 0))) "Rust development environment" tar ((:commit . "6ae74ee131316a9a37ab74417a6870c3a75aa0b0") (:keywords "languages"))]) + (rustic . [(20240813 1515) ((emacs (26 1)) (rust-mode (1 0 3)) (dash (2 13 0)) (f (0 18 2)) (let-alist (1 0 4)) (markdown-mode (2 3)) (project (0 3 0)) (s (1 10 0)) (spinner (1 7 3)) (xterm-color (1 6)) (flycheck (34 0))) "Rust development environment" tar ((:commit . "db1899f911886e2a6a6a522667ce3dbef3f019a9") (:keywords "languages"))]) (rutils . [(20220619 1421) ((emacs (26 1)) (ess (18 10 1)) (transient (0 3 0))) "R utilities with transient" tar ((:commit . "dd500ab8062ce40cb339ec8620bdfc63fdd28364") (:authors ("Shuguang Sun" . "shuguang79@qq.com")) (:maintainers ("Shuguang Sun" . "shuguang79@qq.com")) (:maintainer "Shuguang Sun" . "shuguang79@qq.com") (:keywords "convenience") (:url . "https://github.com/ShuguangSun/rutils.el"))]) (rvm . [(20220910 1558) nil "Emacs integration for rvm" tar ((:commit . "e1e83b5466c132c066142ac63729ba833c530c83") (:authors ("Yves Senn" . "yves.senn@gmx.ch")) (:maintainers ("Yves Senn" . "yves.senn@gmx.ch")) (:maintainer "Yves Senn" . "yves.senn@gmx.ch") (:keywords "ruby" "rvm") (:url . "http://www.emacswiki.org/emacs/RvmEl"))]) (ryo-modal . [(20221221 1355) ((emacs (25 1))) "Roll your own modal mode" tar ((:commit . "b9e6a0f33b9e2aeb6088accd23ed312083d8f707") (:authors ("Erik Sjöstrand" . "sjostrand.erik@gmail.com")) (:maintainers ("Erik Sjöstrand" . "sjostrand.erik@gmail.com")) (:maintainer "Erik Sjöstrand" . "sjostrand.erik@gmail.com") (:keywords "convenience" "modal" "keys") (:url . "http://github.com/Kungsgeten/ryo-modal"))]) @@ -4711,7 +4711,7 @@ (scad-preview . [(20211212 1128) ((scad-mode (91 0)) (emacs (24 4))) "Preview SCAD models in real-time within Emacs" tar ((:commit . "c5449b26c63f3e0a695905a7e4e84f8d844f761b") (:url . "https://zk-phi.github.io/"))]) (scala-mode . [(20240729 420) ((emacs (24 2))) "Major mode for editing Scala" tar ((:commit . "bd0638c32ab0f2eadacf2809329abf5388211760") (:keywords "languages") (:url . "https://github.com/hvesalai/emacs-scala-mode"))]) (scala-repl . [(20240427 1456) ((emacs (29 1))) "Scala REPL Mode" tar ((:commit . "679bdf663e0b32a5a285d6f98daa2e3d5de60289") (:authors ("Daian YUE" . "sheepduke@gmail.com")) (:maintainers ("Daian YUE" . "sheepduke@gmail.com")) (:maintainer "Daian YUE" . "sheepduke@gmail.com") (:keywords "languages" "tools") (:url . "https://github.com/sheepduke/scala-repl.el"))]) - (scala-ts-mode . [(20240630 1733) ((emacs (29 1))) "Scala Tree-Sitter Mode" tar ((:commit . "8c4ace721fb525d1a790121a8625bc2623c9f207") (:authors ("Karan Ahlawat" . "ahlawatkaran12@gmail.com")) (:maintainers ("Karan Ahlawat" . "ahlawatkaran12@gmail.com")) (:maintainer "Karan Ahlawat" . "ahlawatkaran12@gmail.com") (:keywords "scala" "languages" "tree-sitter") (:url . "https://github.com/KaranAhlawat/scala-ts-mode"))]) + (scala-ts-mode . [(20240812 1639) ((emacs (29 1))) "Scala Tree-Sitter Mode" tar ((:commit . "3580c0c724df06af23dff986f0a7a400cbc58cb8") (:authors ("Karan Ahlawat" . "ahlawatkaran12@gmail.com")) (:maintainers ("Karan Ahlawat" . "ahlawatkaran12@gmail.com")) (:maintainer "Karan Ahlawat" . "ahlawatkaran12@gmail.com") (:keywords "scala" "languages" "tree-sitter") (:url . "https://github.com/KaranAhlawat/scala-ts-mode"))]) (scf-mode . [(20151122 248) nil "shorten file-names in compilation type buffers" tar ((:commit . "dbfcdcd89034f208d65e181af58e0d73ad09f8b2") (:keywords "compilation") (:url . "https://github.com/lewang/scf-mode"))]) (scheme-complete . [(20201112 442) nil "Smart auto completion for Scheme in Emacs" tar ((:commit . "b9a1448c4696f117d9ea4e59b6162dc31112e71a"))]) (scholar-import . [(20230412 1413) ((emacs (26 1)) (org (9 0)) (request (0 3 0)) (s (1 10 0)) (parsebib (4 2))) "Import Bibtex & PDF from Google Scholar" tar ((:commit . "2456367578caa7fd768e30238ce080687faa0a25") (:authors ("Anh T Nguyen" . "https://github.com/teeann")) (:maintainers ("Anh T Nguyen" . "https://github.com/teeann")) (:maintainer "Anh T Nguyen" . "https://github.com/teeann") (:url . "https://github.com/teeann/scholar-import"))]) @@ -4751,7 +4751,7 @@ (selcand . [(20240430 1408) ((emacs (25 1))) "Select a candidate from a tree of hint characters" tar ((:commit . "6baa1771eacbcfe7ec854362bed17baea865424e") (:maintainers ("concat \"erjoalgo\" \"@\" \"gmail\" \".com\"" . "")) (:maintainer "concat \"erjoalgo\" \"@\" \"gmail\" \".com\"" . "") (:keywords "lisp" "completing-read" "prompt" "combinations" "vimium") (:url . "https://github.com/erjoalgo/selcand"))]) (select-themes . [(20160221 106) nil "Color theme selection with completing-read" tar ((:commit . "236f54287519a3ea6dd7b3992d053e4f4ff5d0fe") (:authors ("Jason Milkins" . "jasonm23@gmail.com")) (:maintainers ("Jason Milkins" . "jasonm23@gmail.com")) (:maintainer "Jason Milkins" . "jasonm23@gmail.com") (:url . "https://github.com/jasonm23/emacs-select-themes"))]) (selected . [(20230219 1328) nil "Keymap for when region is active" tar ((:commit . "1ca6e12f456caa1dc97c3d68597598662eb5de9a") (:keywords "convenience") (:url . "http://github.com/Kungsgeten/selected.el"))]) - (selected-window-accent-mode . [(20240803 910) ((emacs (28 1)) (transient (0 1 0))) "Accent Selected Window" tar ((:commit . "ab6508654066ec96d20b7848ecb77a61f509b963") (:authors ("James Dyer" . "captainflasmr@gmail.com")) (:maintainers ("James Dyer" . "captainflasmr@gmail.com")) (:maintainer "James Dyer" . "captainflasmr@gmail.com") (:keywords "convenience") (:url . "https://github.com/captainflasmr/selected-window-accent-mode"))]) + (selected-window-accent-mode . [(20240813 1915) ((emacs (28 1)) (transient (0 1 0))) "Accent Selected Window" tar ((:commit . "2affe5bc985fd75a158567b27d064df765c121f3") (:authors ("James Dyer" . "captainflasmr@gmail.com")) (:maintainers ("James Dyer" . "captainflasmr@gmail.com")) (:maintainer "James Dyer" . "captainflasmr@gmail.com") (:keywords "convenience") (:url . "https://github.com/captainflasmr/selected-window-accent-mode"))]) (selectric-mode . [(20200209 2107) nil "IBM Selectric mode for Emacs" tar ((:commit . "bb9e66678f34e9bc23624ff6292cf5e7857e8e5f") (:authors ("Ricardo Bánffy" . "rbanffy@gmail.com")) (:maintainers ("Ricardo Banffy" . "rbanffy@gmail.com")) (:maintainer "Ricardo Banffy" . "rbanffy@gmail.com") (:keywords "multimedia" "convenience" "typewriter" "selectric") (:url . "https://github.com/rbanffy/selectric-mode"))]) (selectrum . [(20220513 2106) ((emacs (26 1))) "Easily select item from list" tar ((:commit . "810ea697bdd559d97b86b795e01769cddfa3daf2") (:authors ("Radian LLC" . "contact+selectrum@radian.codes")) (:maintainers ("Radian LLC" . "contact+selectrum@radian.codes")) (:maintainer "Radian LLC" . "contact+selectrum@radian.codes") (:keywords "extensions") (:url . "https://github.com/radian-software/selectrum"))]) (selectrum-prescient . [(20240803 2320) ((emacs (25 1)) (prescient (6 1 0)) (selectrum (3 1))) "prescient.el + Selectrum" tar ((:commit . "2b8a8b41228bddb2e11eb1c200e98a9edd04797c") (:authors ("Radian LLC" . "contact+prescient@radian.codes")) (:maintainers ("Radian LLC" . "contact+prescient@radian.codes")) (:maintainer "Radian LLC" . "contact+prescient@radian.codes") (:keywords "extensions") (:url . "https://github.com/raxod502/prescient.el"))]) @@ -4796,7 +4796,7 @@ (shell-current-directory . [(20140101 2354) nil "create new shell based on buffer directory" tar ((:commit . "bf843771bf9a4aa05e054ade799eb8862f3be89a") (:keywords "shell" "comint"))]) (shell-here . [(20220102 1703) nil "Open a shell relative to the working directory" tar ((:commit . "eeb437ff26d62a5009046b1b3b4503b768e3131a") (:authors ("Ian Eure" . "ian.eure@gmail.com")) (:maintainers ("Ian Eure" . "ian.eure@gmail.com")) (:maintainer "Ian Eure" . "ian.eure@gmail.com") (:keywords "unix" "tools" "processes"))]) (shell-history . [(20100505 839) nil "integration with shell history" tar ((:commit . "ee371a81f2d2bf5a308344078329ca1e9b5ed38c") (:authors ("rubikitch" . "rubikitch@ruby-lang.org")) (:maintainers ("rubikitch" . "rubikitch@ruby-lang.org")) (:maintainer "rubikitch" . "rubikitch@ruby-lang.org") (:keywords "processes" "convenience") (:url . "http://www.emacswiki.org/cgi-bin/wiki/download/shell-history.el"))]) - (shell-maker . [(20240806 1414) ((emacs (27 1))) "Interaction mode for making comint shells" tar ((:commit . "e42e4d42b8978cdf8d7e7e8a5b7936cba80a035a") (:url . "https://github.com/xenodium/chatgpt-shell"))]) + (shell-maker . [(20240814 938) ((emacs (27 1))) "Interaction mode for making comint shells" tar ((:commit . "6fb85746bd0d316c97e375dcc14eb5314778e74d") (:url . "https://github.com/xenodium/chatgpt-shell"))]) (shell-pop . [(20231228 612) ((emacs (24 1)) (cl-lib (0 5))) "helps you to use shell easily on Emacs. Only one key action to work." tar ((:commit . "ff3dc705ee1c7bc566b35c17e4635c57061fe3ae") (:authors ("Kazuo YAGI" . "kazuo.yagi@gmail.com")) (:maintainers ("Kazuo YAGI" . "kazuo.yagi@gmail.com")) (:maintainer "Kazuo YAGI" . "kazuo.yagi@gmail.com") (:keywords "shell" "terminal" "tools") (:url . "http://github.com/kyagi/shell-pop-el"))]) (shell-split-string . [(20151224 1008) nil "Split strings using shell-like syntax" tar ((:commit . "19f6f999c33cc66a4c91bacdcc3697c25d97bf5a") (:authors ("10sr" . "8.slashes+el[at]gmail[dot]com")) (:maintainers ("10sr" . "8.slashes+el[at]gmail[dot]com")) (:maintainer "10sr" . "8.slashes+el[at]gmail[dot]com") (:keywords "utility" "library" "shell" "string") (:url . "https://github.com/10sr/shell-split-string-el"))]) (shell-switcher . [(20210509 1045) ((emacs (24))) "Provide fast switching between shell buffers." tar ((:commit . "ed74b20fa12935be0068765f5bc8de97b92a8020") (:authors ("Damien Cassou" . "damien.cassou@gmail.com")) (:maintainer "Damien Cassou" . "damien.cassou@gmail.com") (:keywords "emacs" "package" "elisp" "shell" "eshell" "term" "switcher") (:url . "https://github.com/DamienCassou/shell-switcher"))]) @@ -4820,7 +4820,7 @@ (showtip . [(20090830 1040) nil "Show tip at cursor" tar ((:commit . "930da302809a4257e8d69425455b29e1cc91949b") (:authors ("Ye Wenbin" . "wenbinye@gmail.com")) (:maintainers ("Ye Wenbin" . "wenbinye@gmail.com")) (:maintainer "Ye Wenbin" . "wenbinye@gmail.com") (:keywords "help"))]) (shpec-mode . [(20150530 922) nil "Minor mode for shpec specification" tar ((:commit . "76bccd63e3b70233a6c9ca0798dd03550952cc76") (:authors ("AdrieanKhisbe" . "adriean.khisbe@live.fr")) (:maintainers ("AdrieanKhisbe" . "adriean.khisbe@live.fr")) (:maintainer "AdrieanKhisbe" . "adriean.khisbe@live.fr") (:keywords "languages" "tools") (:url . "http://github.com/shpec/shpec-mode"))]) (shr-tag-pre-highlight . [(20240515 1420) ((emacs (25 1)) (language-detection (0 1 0))) "Syntax highlighting code block in HTML" tar ((:commit . "af8ae8d558d1e26d276130c100e02746278ce037") (:authors ("Chunyang Xu" . "mail@xuchunyang.me")) (:maintainers ("Chunyang Xu" . "mail@xuchunyang.me")) (:maintainer "Chunyang Xu" . "mail@xuchunyang.me") (:keywords "html") (:url . "https://github.com/xuchunyang/shr-tag-pre-highlight.el"))]) - (shrface . [(20240810 335) ((emacs (25 1)) (org (9 0)) (language-detection (0 1 0))) "Extend shr/eww with org features and analysis capability" tar ((:commit . "715cb8aa5ff0ff8b02353f9cf3fbc72f0942c998") (:authors ("Damon Chan" . "elecming@gmail.com")) (:maintainers ("Damon Chan" . "elecming@gmail.com")) (:maintainer "Damon Chan" . "elecming@gmail.com") (:keywords "faces") (:url . "https://github.com/chenyanming/shrface"))]) + (shrface . [(20240812 653) ((emacs (25 1)) (org (9 0)) (language-detection (0 1 0))) "Extend shr/eww with org features and analysis capability" tar ((:commit . "71b17066edc8b49d11571ec94e339a93ab2f189c") (:authors ("Damon Chan" . "elecming@gmail.com")) (:maintainers ("Damon Chan" . "elecming@gmail.com")) (:maintainer "Damon Chan" . "elecming@gmail.com") (:keywords "faces") (:url . "https://github.com/chenyanming/shrface"))]) (shrink-path . [(20190208 1335) ((emacs (24)) (s (1 6 1)) (dash (1 8 0)) (f (0 10 0))) "fish-style path" tar ((:commit . "c14882c8599aec79a6e8ef2d06454254bb3e1e41") (:url . "https://gitlab.com/bennya/shrink-path.el"))]) (shrink-whitespace . [(20181003 321) nil "Whitespace removal DWIM key" tar ((:commit . "0407b89c142bd17e65edb666f35e2c6755bd0867") (:authors ("Jean-Christophe Petkovich" . "jcpetkovich@gmail.com")) (:maintainers ("Jean-Christophe Petkovich" . "jcpetkovich@gmail.com")) (:maintainer "Jean-Christophe Petkovich" . "jcpetkovich@gmail.com") (:keywords "convenience") (:url . "https://gitlab.com/jcpetkovich/shrink-whitespace.el"))]) (shroud . [(20210220 1952) ((emacs (25)) (epg (1 0 0)) (s (1 6 0)) (bui (1 2 0)) (dash (2 18 0))) "Shroud secrets" tar ((:commit . "2e6ff2bab4a1e798c090c9d7fbd90b7f3463d5c5") (:authors ("Amar Singh" . "nly@disroot.org")) (:maintainers ("Amar Singh" . "nly@disroot.org")) (:maintainer "Amar Singh" . "nly@disroot.org") (:keywords "tools" "password") (:url . "https://github.com/o-nly/emacs-shroud"))]) @@ -4877,7 +4877,7 @@ (slime-repl-ansi-color . [(20230214 1453) ((emacs (24)) (slime (2 3 1))) "Turn on ANSI colors in REPL output;" tar ((:commit . "9e8af90490332217e45d7568f1690df3f4e25d4b") (:authors ("Max Mikhanosha" . "max@openchat.com")) (:maintainers ("Augustin Fabre" . "augustin@augfab.fr")) (:maintainer "Augustin Fabre" . "augustin@augfab.fr") (:keywords "lisp") (:url . "https://gitlab.com/augfab/slime-repl-ansi-color"))]) (slime-theme . [(20170808 1322) ((emacs (24 0))) "an Emacs 24 theme based on Slime (tmTheme)" tar ((:commit . "8e5880ac69e0b6a079103001cc3a90bdb688998f") (:url . "https://github.com/emacsfodder/tmtheme-to-deftheme"))]) (slime-volleyball . [(20190701 1624) nil "An SVG Slime Volleyball Game" tar ((:commit . "6c135ad18897c3566d4dadfe847061532600ba2e") (:authors ("Thomas Fitzsimmons" . "fitzsim@fitzsim.org")) (:maintainers ("Thomas Fitzsimmons" . "fitzsim@fitzsim.org")) (:maintainer "Thomas Fitzsimmons" . "fitzsim@fitzsim.org") (:keywords "games"))]) - (slint-mode . [(20230922 1143) ((emacs (24 4)) (lsp-mode (6 0))) "Major-mode for the Slint UI language" tar ((:commit . "1ef68ca9cf0cffd2c863c3135f96202a19cf8182") (:authors ("Niklas Cathor" . "niklas.cathor@gmx.de")) (:maintainers ("Niklas Cathor" . "niklas.cathor@gmx.de")) (:maintainer "Niklas Cathor" . "niklas.cathor@gmx.de") (:keywords "languages") (:url . "https://github.com/nilclass/slint-mode"))]) + (slint-mode . [(20240429 1333) ((emacs (24 4))) "Major-mode for the Slint UI language" tar ((:commit . "168a6cfb90b5e36360074c83f80d5bbac2f0287e") (:authors ("Niklas Cathor" . "niklas.cathor@gmx.de")) (:maintainers ("Niklas Cathor" . "niklas.cathor@gmx.de")) (:maintainer "Niklas Cathor" . "niklas.cathor@gmx.de") (:keywords "languages") (:url . "https://github.com/nilclass/slint-mode"))]) (slirm . [(20160201 1425) ((emacs (24 4))) "Systematic Literature Review Mode for Emacs." tar ((:commit . "9adfbe1fc67580e7d0d90f7e927a25d63a797464") (:authors ("Florian Biermann" . "fbie@itu.dk")) (:maintainers ("Florian Biermann" . "fbie@itu.dk")) (:maintainer "Florian Biermann" . "fbie@itu.dk") (:url . "http://github.com/fbie/slirm"))]) (slovak-holidays . [(20211018 1754) nil "Adds a list of slovak holidays to Emacs calendar" tar ((:commit . "bedd26dd45ca497c0028a11e94a905560fcdb2f1") (:authors ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainers ("Matúš Goljer" . "matus.goljer@gmail.com")) (:maintainer "Matúš Goljer" . "matus.goljer@gmail.com") (:keywords "calendar"))]) (slow-keys . [(20220807 1425) ((emacs (24 1))) "Slow keys mode to avoid RSI" tar ((:commit . "b951ae4bdcea56ced03f227b82b28c3d91d15e61") (:authors ("Manuel Uberti" . "manuel.uberti@inventati.org")) (:maintainers ("Manuel Uberti" . "manuel.uberti@inventati.org")) (:maintainer "Manuel Uberti" . "manuel.uberti@inventati.org") (:keywords "convenience") (:url . "https://github.com/manuel-uberti/slow-keys"))]) @@ -4941,7 +4941,7 @@ (snapshot-timemachine . [(20161221 929) ((emacs (24 4))) "Step through (Btrfs, ZFS, ...) snapshots of files" tar ((:commit . "99efcebab309b11ed512a8dc62555d3834df5efb") (:authors ("Thomas Winant" . "dewinant@gmail.com")) (:maintainers ("Thomas Winant" . "dewinant@gmail.com")) (:maintainer "Thomas Winant" . "dewinant@gmail.com") (:url . "https://github.com/mrBliss/snapshot-timemachine"))]) (snapshot-timemachine-rsnapshot . [(20170324 1213) ((snapshot-timemachine (20160222 132)) (seq (2 19))) "rsnapshot backend for snapshot-timemachine" tar ((:commit . "72b0b700d80f1a0442e62bbbb6a0c8c59182f97f") (:authors ("Nicolas Petton" . "nicolas@petton.fr")) (:maintainers ("Nicolas Petton" . "nicolas@petton.fr")) (:maintainer "Nicolas Petton" . "nicolas@petton.fr"))]) (snazzy-theme . [(20170828 757) ((emacs (24)) (base16-theme (2 1))) "An elegant syntax theme with bright colors" tar ((:commit . "578d7ebc4ed91c0a630b652c4b6fdd54d9ae16cd") (:keywords "faces" "theme" "color" "snazzy") (:url . "https://github.com/weijiangan/emacs-snazzy/"))]) - (sniem . [(20240804 908) ((emacs (27 1)) (s (2 12 0)) (dash (1 12 0))) "Hands-eased united editing method" tar ((:commit . "fc8b478da6690fb6cf3e8f6afa0f38de2b3f01c2") (:keywords "convenience" "united-editing-method") (:url . "https://github.com/SpringHan/sniem.git"))]) + (sniem . [(20240814 931) ((emacs (27 1)) (s (2 12 0)) (dash (1 12 0))) "Hands-eased united editing method" tar ((:commit . "e57e3b2afdabfa3ed02307f2f8839c822d680f92") (:keywords "convenience" "united-editing-method") (:url . "https://github.com/SpringHan/sniem.git"))]) (snitch . [(20210202 1730) ((emacs (27 1))) "An Emacs firewall" tar ((:commit . "3b3e7f1bf612c4624764d1ec4b1a96e4d2850b05") (:authors ("Trevor Bentley" . "snitch.el@x.mrmekon.com")) (:maintainers ("Trevor Bentley" . "snitch.el@x.mrmekon.com")) (:maintainer "Trevor Bentley" . "snitch.el@x.mrmekon.com") (:keywords "processes" "comm") (:url . "https://github.com/mrmekon/snitch-el"))]) (snoopy . [(20171008 2004) ((emacs (24)) (cl-lib (0 6))) "minor mode for number row unshifted character insertion" tar ((:commit . "ec4123bdebfe0bb7bf4feaac2dc02b59caffe386") (:authors ("António Nuno Monteiro" . "anmonteiro@gmail.com")) (:maintainers ("António Nuno Monteiro" . "anmonteiro@gmail.com")) (:maintainer "António Nuno Monteiro" . "anmonteiro@gmail.com") (:keywords "lisp"))]) (snow . [(20221226 2238) ((emacs (26 3))) "Let it snow in Emacs!" tar ((:commit . "be17977677fa29709a726715a1a1cba1bd299f68") (:authors ("Adam Porter" . "adam@alphapapa.net")) (:maintainers ("Adam Porter" . "adam@alphapapa.net")) (:maintainer "Adam Porter" . "adam@alphapapa.net") (:keywords "games") (:url . "https://github.com/alphapapa/snow.el"))]) @@ -4986,7 +4986,7 @@ (sparkline . [(20150101 1319) ((cl-lib (0 3))) "Make sparkline images from a list of numbers" tar ((:commit . "a2b5d817d272d6363b67ed8f8cc75499a19fa8d2") (:authors ("Willem Rein Oudshoorn" . "woudshoo@xs4all.nl")) (:maintainers ("Willem Rein Oudshoorn" . "woudshoo@xs4all.nl")) (:maintainer "Willem Rein Oudshoorn" . "woudshoo@xs4all.nl") (:keywords "extensions"))]) (sparql-mode . [(20230104 1113) ((cl-lib (0 5)) (emacs (24 3))) "Edit and interactively evaluate SPARQL queries." tar ((:commit . "1f6196094ec6626722c6e03a13f6844c68f62703") (:authors ("Craig Andera" . "canderaatwangderadotcom")) (:maintainers ("Bjarte Johansen" . "BjartedotJohansenatgmaildotcom")) (:maintainer "Bjarte Johansen" . "BjartedotJohansenatgmaildotcom") (:url . "https://github.com/ljos/sparql-mode"))]) (spatial-navigate . [(20240421 908) ((emacs (29 1))) "Directional navigation between white-space blocks" tar ((:commit . "4f85fe3ae4d240a35d3d7edd8b865612024f9dda") (:authors ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainers ("Campbell Barton" . "ideasman42@gmail.com")) (:maintainer "Campbell Barton" . "ideasman42@gmail.com") (:url . "https://codeberg.org/ideasman42/emacs-spatial-navigate"))]) - (spdx . [(20240809 111) ((emacs (24 4))) "Insert SPDX license and copyright headers" tar ((:commit . "dbfb7ea8b5fcc3d8825cc332f49a12b764bae5e3") (:authors ("Zhiwei Chen" . "condy0919@gmail.com")) (:maintainers ("Zhiwei Chen" . "condy0919@gmail.com")) (:maintainer "Zhiwei Chen" . "condy0919@gmail.com") (:keywords "license" "tools") (:url . "https://github.com/condy0919/spdx.el"))]) + (spdx . [(20240814 111) ((emacs (24 4))) "Insert SPDX license and copyright headers" tar ((:commit . "15ea8f7e4b08fd8322cfb2f33fb85a9736e92da3") (:authors ("Zhiwei Chen" . "condy0919@gmail.com")) (:maintainers ("Zhiwei Chen" . "condy0919@gmail.com")) (:maintainer "Zhiwei Chen" . "condy0919@gmail.com") (:keywords "license" "tools") (:url . "https://github.com/condy0919/spdx.el"))]) (speech-tagger . [(20170728 1829) ((cl-lib (0 5))) "tag parts of speech using coreNLP" tar ((:commit . "61955b40d4e8b09e66a3e8033e82893f81657c06") (:authors ("Danny McClanahan" . "danieldmcclanahan@gmail.com")) (:maintainers ("Danny McClanahan" . "danieldmcclanahan@gmail.com")) (:maintainer "Danny McClanahan" . "danieldmcclanahan@gmail.com") (:keywords "speech" "tag" "nlp" "language" "corenlp" "parsing" "natural") (:url . "https://github.com/cosmicexplorer/speech-tagger"))]) (speechd-el . [(20240513 1716) nil "Client to speech synthesizers and Braille displays." tar ((:commit . "ac7497e394bf7d46e0b2c27570f5507f6a50a157") (:authors ("Milan Zamazal" . "pdm@zamazal.org")) (:maintainer "Milan Zamazal" . "pdm@zamazal.org") (:url . "https://github.com/brailcom/speechd-el"))]) (speed-type . [(20230926 838) ((emacs (26 1)) (compat (29 1 3))) "Practice touch and speed typing" tar ((:commit . "28b8e8c1cc24511758168f30bcac18d8fb93706d") (:maintainers ("Daniel Kraus" . "daniel@kraus.my")) (:maintainer "Daniel Kraus" . "daniel@kraus.my") (:keywords "games") (:url . "https://github.com/dakra/speed-type"))]) @@ -5022,7 +5022,7 @@ (sr-speedbar . [(20220705 1231) nil "Same frame speedbar" tar ((:commit . "73ecfc21cf38f0cb1dfbbebebdc3cf573eccf7d2") (:authors ("Sebastian Rose" . "sebastian_rose@gmx.de")) (:maintainers ("Sebastian Rose" . "sebastian_rose@gmx.de") ("Peter Lunicks" . "plunix@users.sourceforge.net")) (:maintainer "Sebastian Rose" . "sebastian_rose@gmx.de") (:keywords "speedbar" "sr-speedbar.el") (:url . "http://www.emacswiki.org/emacs/download/sr-speedbar.el"))]) (srcery-theme . [(20240220 805) ((emacs (24))) "Dark color theme" tar ((:commit . "60028633c5722e6b8ea12844618be0e9b31be55a") (:keywords "faces") (:url . "https://github.com/srcery-colors/srcery-emacs"))]) (srefactor . [(20230504 617) ((emacs (24 4))) "A refactoring tool based on Semantic parser framework" tar ((:commit . "95c70a94b5aad4c85b35569e2f2325047791153a") (:authors ("Do Hoang" . "tuhdo1710@gmail.com")) (:keywords "c" "languages" "tools") (:url . "https://github.com/tuhdo/semantic-refactor"))]) - (srfi . [(20240507 58) ((emacs (25 1))) "Scheme Requests for Implementation browser" tar ((:commit . "7d3584b0b887a3bedebe5fd829fa7623c1d730fc") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainers ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "languages" "util") (:url . "https://github.com/srfi-explorations/emacs-srfi"))]) + (srfi . [(20240813 1909) ((emacs (25 1))) "Scheme Requests for Implementation browser" tar ((:commit . "3dac6e04eb2ed797a617abf6b3288808e83d66b4") (:authors ("Lassi Kortela" . "lassi@lassi.io")) (:maintainers ("Lassi Kortela" . "lassi@lassi.io")) (:maintainer "Lassi Kortela" . "lassi@lassi.io") (:keywords "languages" "util") (:url . "https://github.com/srfi-explorations/emacs-srfi"))]) (srv . [(20180715 1959) ((emacs (24 3))) "perform SRV DNS requests" tar ((:commit . "714387d5a5cf34d8d8cd96bdb1f9cb8ded823ff7") (:authors ("Magnus Henoch" . "magnus.henoch@gmail.com")) (:maintainers ("Magnus Henoch" . "magnus.henoch@gmail.com")) (:maintainer "Magnus Henoch" . "magnus.henoch@gmail.com") (:keywords "comm") (:url . "https://github.com/legoscia/srv.el"))]) (ssass-mode . [(20200211 132) ((emacs (24 3))) "Edit Sass without a Turing Machine" tar ((:commit . "96f557887ad97a0066a60c54f92b7234b8407016") (:authors ("Adam Niederer" . "adam.niederer@gmail.com")) (:maintainers ("Adam Niederer" . "adam.niederer@gmail.com")) (:maintainer "Adam Niederer" . "adam.niederer@gmail.com") (:keywords "languages" "sass") (:url . "http://github.com/AdamNiederer/ssass-mode"))]) (ssh . [(20120904 2042) nil "Support for remote logins using ssh." tar ((:commit . "c17cf5b43df8ac4662a0580f85898e1f078df0d1") (:authors ("Noah Friedman" . "friedman@splode.com")) (:maintainers ("Ian Eure" . "ian.eure@gmail.com")) (:maintainer "Ian Eure" . "ian.eure@gmail.com") (:keywords "unix" "comm"))]) @@ -5056,7 +5056,7 @@ (streamlink . [(20210811 1429) ((s (1 12 0))) "A major mode for streamlink output" tar ((:commit . "13dff15121ac0276f693696db9b04ae5820058d5") (:keywords "multimedia" "streamlink") (:url . "https://github.com/BenediktBroich/streamlink"))]) (strie . [(20160211 2222) ((cl-lib (0 5))) "A simple trie data structure implementation" tar ((:commit . "eb7efb0cccc127c414f6a64db11454869d9c10a8") (:authors ("James Atwood" . "jatwood@cs.umass.edu")) (:maintainers ("James Atwood" . "jatwood@cs.umass.edu")) (:maintainer "James Atwood" . "jatwood@cs.umass.edu"))]) (string-edit-at-point . [(20230118 1933) ((dash (1 2 0))) "Avoid escape nightmares by editing string in separate buffer" tar ((:commit . "87936d816ae24184dd83688136531b6b6f1943fe") (:authors ("Magnar Sveen" . "magnars@gmail.com")) (:maintainers ("Magnar Sveen" . "magnars@gmail.com")) (:maintainer "Magnar Sveen" . "magnars@gmail.com"))]) - (string-inflection . [(20240809 2317) nil "underscore -> UPCASE -> CamelCase -> lowerCamelCase conversion of names" tar ((:commit . "fcd47084cb0e3c5348a6b66eb8b9a0c3ea649657") (:authors ("akicho8" . "akicho8@gmail.com")) (:maintainers ("akicho8" . "akicho8@gmail.com")) (:maintainer "akicho8" . "akicho8@gmail.com") (:keywords "elisp"))]) + (string-inflection . [(20240811 2252) nil "underscore -> UPCASE -> CamelCase -> lowerCamelCase conversion of names" tar ((:commit . "fa787ae106e0b4c6af4c3554108a4a4a7bf7f60e") (:authors ("akicho8" . "akicho8@gmail.com")) (:maintainers ("akicho8" . "akicho8@gmail.com")) (:maintainer "akicho8" . "akicho8@gmail.com") (:keywords "elisp"))]) (string-utils . [(20140508 2041) ((list-utils (0 4 2))) "String-manipulation utilities" tar ((:commit . "8b56e1f79d2de46d1e9b5e24d889e9f4c3cc85d4") (: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/string-utils"))]) (stripe-buffer . [(20141208 1508) ((cl-lib (1 0))) "Use a different background for even and odd lines" tar ((:commit . "c252080f55cb78c951b19ebab9687f6d00237baf") (:authors ("Andy Stewart" . "lazycat.manatee@gmail.com")) (:maintainers ("sabof" . "esabof@gmail.com")) (:maintainer "sabof" . "esabof@gmail.com") (:url . "https://github.com/sabof/stripe-buffer"))]) (stripes . [(20230402 1228) ((emacs (24 3))) "highlight alternating lines differently" tar ((:commit . "4683c9020da14bb1c1f74b90d27a4d9fdc7a9147") (:authors ("Michael Schierl" . "schierlm-public@gmx.de") ("těpán Němec" . "stepnem@smrk.net")) (:maintainers ("těpán Němec" . "stepnem@smrk.net")) (:maintainer "těpán Němec" . "stepnem@smrk.net") (:keywords "convenience" "faces") (:url . "http://git.smrk.net/stripes.el"))]) @@ -5102,7 +5102,7 @@ (sweetgreen . [(20180605 335) ((dash (2 12 1)) (helm (1 5 6)) (request (0 2 0)) (cl-lib (0 5))) "Order Salads from sweetgreen.com" tar ((:commit . "e933fe466b5ef0e976967e203f88bd7a012469d1") (:authors ("Diego Berrocal" . "cestdiego@gmail.com")) (:maintainers ("Diego Berrocal" . "cestdiego@gmail.com")) (:maintainer "Diego Berrocal" . "cestdiego@gmail.com") (:keywords "salad" "food" "sweetgreen" "request") (:url . "https://www.github.com/CestDiego/sweetgreen.el"))]) (swift-helpful . [(20220707 846) ((emacs (25 1)) (dash (2 12 0)) (lsp-mode (6 0)) (swift-mode (8 0 0))) "Show documentation for Swift programs." tar ((:commit . "b46c580e4b8f55761431ec677866de3fc66592e9") (:authors ("Daniel Martín" . "mardani29@yahoo.es")) (:maintainers ("Daniel Martín" . "mardani29@yahoo.es")) (:maintainer "Daniel Martín" . "mardani29@yahoo.es") (:keywords "help" "swift") (:url . "https://github.com/danielmartin/swift-helpful"))]) (swift-mode . [(20240622 935) ((emacs (24 4)) (seq (2 3))) "Major-mode for Apple's Swift programming language" tar ((:commit . "b06c97b909418b5dca3d15351b2ce86a04df32fb") (:authors ("taku0" . "mxxouy6x3m_github@tatapa.org") ("Chris Barrett" . "chris.d.barrett@me.com") ("Bozhidar Batsov" . "bozhidar@batsov.com") ("Arthur Evstifeev" . "lod@pisem.net")) (:maintainers ("taku0" . "mxxouy6x3m_github@tatapa.org")) (:maintainer "taku0" . "mxxouy6x3m_github@tatapa.org") (:keywords "languages" "swift") (:url . "https://github.com/swift-emacs/swift-mode"))]) - (swift-ts-mode . [(20240810 1324) ((emacs (29 1))) "Major mode for Swift based on tree-sitter" tar ((:commit . "482cdd46c7922ee3dd4f5027b686a5993fb5ae34") (:keywords "swift" "languages" "tree-sitter") (:url . "https://github.com/rechsteiner/swift-ts-mode"))]) + (swift-ts-mode . [(20240811 853) ((emacs (29 1))) "Major mode for Swift based on tree-sitter" tar ((:commit . "e094c17022e5a3fc27cc504fef6138cdde14ad00") (:keywords "swift" "languages" "tree-sitter") (:url . "https://github.com/rechsteiner/swift-ts-mode"))]) (swift3-mode . [(20160918 1250) ((emacs (24 4))) "Major-mode for Apple's Swift programming language." tar ((:commit . "ea34d46bf9a4293e75ffdac9500d34989316d9e9") (:keywords "languages" "swift") (:url . "https://github.com/taku0/swift3-mode"))]) (swiper . [(20240520 1202) ((emacs (24 5)) (ivy (0 14 2))) "Isearch with an overview. Oh, man!" tar ((:commit . "c8808d88c633fdd00f7671fee054954f3a7598b8") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainers ("Basil L. Contovounesios" . "basil@contovou.net")) (:maintainer "Basil L. Contovounesios" . "basil@contovou.net") (:keywords "matching") (:url . "https://github.com/abo-abo/swiper"))]) (swiper-helm . [(20180131 1744) ((emacs (24 1)) (swiper (0 1 0)) (helm (1 5 3))) "Helm version of Swiper." tar ((:commit . "93fb6db87bc6a5967898b5fd3286954cc72a0008") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainers ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:keywords "matching") (:url . "https://github.com/abo-abo/swiper-helm"))]) @@ -5171,7 +5171,7 @@ (tea-time . [(20120331 820) nil "Simple timer package, useful to make perfect tea." tar ((:commit . "1f6cf0bdd27c5eb3508989c5095427781f858eca") (:authors ("konsty" . "antipin.konstantin@googlemail.com")) (:maintainers ("Gabriel Saldana" . "gsaldana@gmail.com")) (:maintainer "Gabriel Saldana" . "gsaldana@gmail.com") (:keywords "timer" "tea-time"))]) (teacode-expand . [(20181231 640) ((emacs (24 4))) "Expansion of text by TeaCode program." tar ((:commit . "7df6f9ec95da1fb47bbae489bb3f2c27ed3a9b3a") (:authors ("Richard Guay" . "raguay@customct.com")) (:maintainers ("Richard Guay" . "raguay@customct.com")) (:maintainer "Richard Guay" . "raguay@customct.com") (:keywords "lisp") (:url . "https://github.com/raguay/TeaCode-Expand"))]) (teco . [(20200707 2309) nil "Teco interpreter" tar ((:commit . "2529eb0f7f35c526c1b6fca5250399718ff5138a") (:authors ("Dale R. Worley" . "worley@alum.mit.edu")) (:maintainers ("Mark T. Kennedy" . "mtk@acm.org")) (:maintainer "Mark T. Kennedy" . "mtk@acm.org") (:keywords "convenience" "emulations" "files") (:url . "https://github.com/mtk/teco.git"))]) - (telega . [(20240527 606) ((emacs (27 1)) (visual-fill-column (1 9)) (rainbow-identifiers (0 2 2)) (transient (0 3 0))) "Telegram client (unofficial)" tar ((:commit . "58b4963b292ceb723d665df100b519eb5a99c676") (:authors ("Zajcev Evgeny" . "zevlg@yandex.ru")) (:maintainers ("Zajcev Evgeny" . "zevlg@yandex.ru")) (:maintainer "Zajcev Evgeny" . "zevlg@yandex.ru") (:keywords "comm") (:url . "https://github.com/zevlg/telega.el"))]) + (telega . [(20240812 911) ((emacs (27 1)) (visual-fill-column (1 9)) (rainbow-identifiers (0 2 2)) (transient (0 3 0))) "Telegram client (unofficial)" tar ((:commit . "fdaa13c65045e61aed1deb3f90adea191131bc77") (:authors ("Zajcev Evgeny" . "zevlg@yandex.ru")) (:maintainers ("Zajcev Evgeny" . "zevlg@yandex.ru")) (:maintainer "Zajcev Evgeny" . "zevlg@yandex.ru") (:keywords "comm") (:url . "https://github.com/zevlg/telega.el"))]) (telepathy . [(20131209 1258) nil "Access Telepathy from Emacs" tar ((:commit . "211d785b02a29ddc254422fdcc3db45262582f8c") (:authors ("Nicolas Petton" . "petton.nicolas@gmail.com")) (:maintainers ("Nicolas Petton" . "petton.nicolas@gmail.com")) (:maintainer "Nicolas Petton" . "petton.nicolas@gmail.com") (:keywords "telepathy" "tools"))]) (telephone-line . [(20240109 2021) ((emacs (24 4)) (cl-lib (0 5)) (cl-generic (0 2)) (seq (1 8))) "Rewrite of Powerline" tar ((:commit . "6016418a5e1e8e006cc202eff50ff28b594eeca4") (:authors ("Daniel Bordak" . "dbordak@fastmail.fm")) (:maintainers ("Daniel Bordak" . "dbordak@fastmail.fm")) (:maintainer "Daniel Bordak" . "dbordak@fastmail.fm") (:keywords "mode-line") (:url . "https://github.com/dbordak/telephone-line"))]) (teleport . [(20240718 652) ((emacs (28 1)) (dash (2 18 0))) "Integration for tsh (goteleport.com)" tar ((:commit . "929f87990a6ee83dfcb7ebf9f8580828f1281ebb") (:authors ("Caramel Hooves" . "caramel.hooves@protonmail.com")) (:maintainers ("Caramel Hooves" . "caramel.hooves@protonmail.com")) (:maintainer "Caramel Hooves" . "caramel.hooves@protonmail.com") (:keywords "tools") (:url . "https://github.com/caramelhooves/teleport.el"))]) @@ -5190,7 +5190,7 @@ (term+mux . [(20140211 749) ((term+ (0 1)) (tab-group (0 1))) "term+ terminal multiplexer and session management" tar ((:commit . "81b60e80cf008472bfd7fad9233af2ef722c208a") (:authors ("INA Lintaro" . "tarao.gnnatgmail.com")) (:maintainers ("INA Lintaro" . "tarao.gnnatgmail.com")) (:maintainer "INA Lintaro" . "tarao.gnnatgmail.com") (:keywords "terminal" "emulation") (:url . "http://github.com/tarao/term+-el"))]) (term-alert . [(20230407 1715) ((emacs (24 0)) (term-cmd (1 1)) (alert (1 1)) (f (0 18 2))) "Notifications when commands complete in term.el." tar ((:commit . "8e7e744773e41355bcd9f5c911001be08bc79bec") (:authors ("Callie Cameron" . "cjcameron7@gmail.com")) (:maintainer "Callie Cameron" . "cjcameron7@gmail.com") (:keywords "notifications" "processes") (:url . "https://github.com/calliecameron/term-alert"))]) (term-cmd . [(20230407 1704) ((emacs (27 2)) (dash (2 12 0)) (f (0 18 2))) "Send commands from programs running in term.el." tar ((:commit . "26c5a8cb6b55ac0d6c6bc08f6ea1b1e53f6e2654") (:authors ("Callie Cameron" . "cjcameron7@gmail.com")) (:maintainer "Callie Cameron" . "cjcameron7@gmail.com") (:keywords "processes") (:url . "https://github.com/calliecameron/term-cmd"))]) - (term-manager . [(20240602 2356) ((dash (2 12 0)) (emacs (24 4))) "Contextual terminal management" tar ((:commit . "25353734c65cd5cc952e4893b552629ca1d0d37f") (:authors ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainers ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "terminals" "tools") (:url . "https://www.github.com/IvanMalison/term-manager"))]) + (term-manager . [(20240811 2337) ((dash (2 12 0)) (emacs (24 4))) "Contextual terminal management" tar ((:commit . "fbf64768902cded6d75261515bd4aafe7cf56111") (:authors ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainers ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "terminals" "tools") (:url . "https://www.github.com/IvanMalison/term-manager"))]) (term-project . [(20240602 2356) ((emacs (28 1)) (term-manager (0 1 0))) "Terminal management for project.el" tar ((:commit . "25353734c65cd5cc952e4893b552629ca1d0d37f") (:authors ("Ivan Malison" . "IvanMalison@gmail.com") ("ROCKTAKEY" . "rocktakey@gmail.com")) (:maintainers ("Ivan Malison" . "IvanMalison@gmail.com") ("ROCKTAKEY" . "rocktakey@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "project" "tools" "terminals" "vc") (:url . "https://www.github.com/IvanMalison/term-manager"))]) (term-projectile . [(20240602 2356) ((emacs (24)) (term-manager (0 1 0)) (projectile (0 13 0))) "projectile terminal management" tar ((:commit . "25353734c65cd5cc952e4893b552629ca1d0d37f") (:authors ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainers ("Ivan Malison" . "IvanMalison@gmail.com")) (:maintainer "Ivan Malison" . "IvanMalison@gmail.com") (:keywords "projectile" "tools" "terminals" "vc") (:url . "https://www.github.com/IvanMalison/term-manager"))]) (term-run . [(20200128 702) nil "Run arbitrary command in terminal buffer" tar ((:commit . "0fd135d55fcf864598b1fb8dd880833a1a322910") (:authors ("10sr" . "8slashes+el[at]gmail[dot]com")) (:maintainers ("10sr" . "8slashes+el[at]gmail[dot]com")) (:maintainer "10sr" . "8slashes+el[at]gmail[dot]com") (:keywords "utility" "shell" "command" "term-mode") (:url . "https://github.com/10sr/term-run-el"))]) @@ -5229,7 +5229,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") (: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 . [(20240805 1320) ((emacs (24))) "major mode for fbthrift and Apache Thrift files" tar ((:commit . "115bc7baa3d43e25f35e70c219a9f75417bc22c0") (:keywords "languages"))]) + (thrift . [(20240811 1631) ((emacs (24))) "major mode for fbthrift and Apache Thrift files" tar ((:commit . "bf6f866e2f4ce7adb35b99c6c126720cd9e2b095") (:keywords "languages"))]) (thumb-through . [(20120119 534) nil "Plain text reader of HTML documents" tar ((:commit . "08d8fb720f93c6172653e035191a8fa9c3305e63") (:keywords "html"))]) (tickscript-mode . [(20171219 203) ((emacs (24 1))) "A major mode for Tickscript files" tar ((:commit . "f0579f38ff14954df5002ce30ae6d4a2c978d461") (:authors ("Marc Sherry" . "msherry@gmail.com")) (:maintainers ("Marc Sherry" . "msherry@gmail.com")) (:maintainer "Marc Sherry" . "msherry@gmail.com") (:keywords "languages") (:url . "https://github.com/msherry/tickscript-mode"))]) (tidal . [(20240407 1952) ((haskell-mode (16)) (emacs (25 1))) "Interact with TidalCycles for live coding patterns" tar ((:commit . "88f09edf6bef2228d5f530dea872b08a9d803066") (:authors (nil . "alex@slab.org")) (:maintainers (nil . "alex@slab.org")) (:maintainer nil . "alex@slab.org") (:keywords "tools") (:url . "https://github.com/tidalcycles/Tidal"))]) @@ -5314,17 +5314,17 @@ (transwin . [(20240126 720) ((emacs (24 3))) "Make window/frame transparent" tar ((:commit . "99f9296a18654cb38f2ffb8682b7532be60bec5e") (:authors ("Jen-Chieh" . "jcs090218@gmail.com")) (:maintainers ("Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Jen-Chieh" . "jcs090218@gmail.com") (:keywords "frames" "window" "transparent") (:url . "https://github.com/jcs-elpa/transwin"))]) (trashed . [(20230811 157) ((emacs (25 1))) "Viewing/editing system trash can" tar ((:commit . "52a52a363ce53855790e7a59aed6976eec18c9ea") (:authors ("Shingo Tanaka" . "shingo.fg8@gmail.com")) (:maintainers ("Shingo Tanaka" . "shingo.fg8@gmail.com")) (:maintainer "Shingo Tanaka" . "shingo.fg8@gmail.com") (:keywords "files" "convenience" "unix") (:url . "https://github.com/shingo256/trashed"))]) (travis . [(20150825 1138) ((s (1 9 0)) (dash (2 9 0)) (pkg-info (0 5 0)) (request (0 1 0))) "Emacs client for Travis" tar ((:commit . "c8769d3db10ed4604969049e3bd276afa0a0138e") (:authors ("Nicolas Lamirault" . "nicolas.lamirault@gmail.com")) (:maintainers ("Nicolas Lamirault" . "nicolas.lamirault@gmail.com")) (:maintainer "Nicolas Lamirault" . "nicolas.lamirault@gmail.com") (:keywords "travis") (:url . "https://github.com/nlamirault/emacs-travis"))]) - (tray . [(20240805 1438) ((emacs (27 1)) (compat (30 0 0 0)) (transient (0 7 0))) "Various transient menus" tar ((:commit . "988afc5967237ed9acfd6d9a53c631e476fccc62") (:authors ("Jonas Bernoulli" . "emacs.tray@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.tray@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.tray@jonas.bernoulli.dev") (:keywords "convenience") (:url . "https://git.sr.ht/~tarsius/tray"))]) + (tray . [(20240811 29) ((emacs (27 1)) (compat (30 0 0 0)) (transient (0 7 4))) "Various transient menus" tar ((:commit . "fca0ed0010473a61be135b2791426ae5463fe9ea") (:authors ("Jonas Bernoulli" . "emacs.tray@jonas.bernoulli.dev")) (:maintainers ("Jonas Bernoulli" . "emacs.tray@jonas.bernoulli.dev")) (:maintainer "Jonas Bernoulli" . "emacs.tray@jonas.bernoulli.dev") (:keywords "convenience") (:url . "https://github.com/tarsius/tray"))]) (tree-edit . [(20231124 1712) ((emacs (29 1)) (dash (2 19)) (reazon (0 4 0)) (s (0 0 0))) "A library for structural refactoring and editing" tar ((:commit . "3e71d276e7369ff4525f0e2b84356a31fe6b7782") (:authors ("Ethan Leba" . "ethanleba5@gmail.com")) (:maintainers ("Ethan Leba" . "ethanleba5@gmail.com")) (:maintainer "Ethan Leba" . "ethanleba5@gmail.com") (:url . "https://github.com/ethan-leba/tree-edit"))]) (tree-mode . [(20151104 1331) nil "A mode to manage tree widgets" tar ((:commit . "b06078826d5875d74b0e7b7ac47b0d0917610534") (:authors (nil . "wenbinye@163.com")) (:maintainers (nil . "wenbinye@163.com")) (:maintainer nil . "wenbinye@163.com") (:keywords "help" "convenience" "widget"))]) (tree-sitter . [(20220212 1632) ((emacs (25 1)) (tsc (0 18 0))) "Incremental parsing system" tar ((:commit . "909717c685ff5a2327fa2ca8fb8a25216129361c") (:authors ("Tuấn-Anh Nguyễn" . "ubolonton@gmail.com")) (:maintainers ("Tuấn-Anh Nguyễn" . "ubolonton@gmail.com")) (:maintainer "Tuấn-Anh Nguyễn" . "ubolonton@gmail.com") (:keywords "languages" "tools" "parsers" "tree-sitter") (:url . "https://github.com/emacs-tree-sitter/elisp-tree-sitter"))]) (tree-sitter-ess-r . [(20221012 855) ((emacs (26 1)) (ess (18 10 1)) (tree-sitter (0 12 1)) (tree-sitter-langs (0 12 0))) "R with tree-sitter" tar ((:commit . "9669c00f3d3463e6769725af74c392891e269eed") (:authors ("Shuguang Sun" . "shuguang79@qq.com")) (:maintainers ("Shuguang Sun" . "shuguang79@qq.com")) (:maintainer "Shuguang Sun" . "shuguang79@qq.com") (:keywords "tools") (:url . "https://github.com/ShuguangSun/tree-sitter-ess-r"))]) (tree-sitter-indent . [(20220411 1439) ((emacs (26 1)) (tree-sitter (0 12 1)) (seq (2 20))) "Provide indentation with a Tree-sitter backend" tar ((:commit . "4ef246db3e4ff99f672fe5e4b416c890f885c09e") (:authors ("Felipe Lema" . "felipelema@mortemale.org")) (:maintainers ("Felipe Lema" . "felipelema@mortemale.org")) (:maintainer "Felipe Lema" . "felipelema@mortemale.org") (:keywords "convenience" "internal") (:url . "https://codeberg.org/FelipeLema/tree-sitter-indent.el"))]) (tree-sitter-ispell . [(20240610 2252) ((emacs (26 1)) (tree-sitter (0 15 0))) "Run ispell on tree-sitter text nodes" tar ((:commit . "a06eff00affff85992d2a8ad0019034747ffeb70") (:authors ("Erick Navarro" . "erick@navarro.io")) (:maintainers ("Erick Navarro" . "erick@navarro.io")) (:maintainer "Erick Navarro" . "erick@navarro.io") (:url . "https://github.com/erickgnavar/tree-sitter-ispell.el"))]) - (tree-sitter-langs . [(20240805 321) ((emacs (25 1)) (tree-sitter (0 15 0))) "Grammar bundle for tree-sitter" tar ((:commit . "0c46e6a226a4d342f03c1b63089d4273a1acf0a9") (:authors ("Tuấn-Anh Nguyễn" . "ubolonton@gmail.com")) (:maintainers ("Tuấn-Anh Nguyễn" . "ubolonton@gmail.com")) (:maintainer "Tuấn-Anh Nguyễn" . "ubolonton@gmail.com") (:keywords "languages" "tools" "parsers" "tree-sitter") (:url . "https://github.com/emacs-tree-sitter/tree-sitter-langs"))]) + (tree-sitter-langs . [(20240811 908) ((emacs (25 1)) (tree-sitter (0 15 0))) "Grammar bundle for tree-sitter" tar ((:commit . "ac3aa507a8a5664159613834fe6568aa86cc16f1") (:authors ("Tuấn-Anh Nguyễn" . "ubolonton@gmail.com")) (:maintainers ("Tuấn-Anh Nguyễn" . "ubolonton@gmail.com")) (:maintainer "Tuấn-Anh Nguyễn" . "ubolonton@gmail.com") (:keywords "languages" "tools" "parsers" "tree-sitter") (:url . "https://github.com/emacs-tree-sitter/tree-sitter-langs"))]) (treebundel . [(20240531 2321) ((emacs (27 1)) (compat (29 1 4 2))) "Bundle related git-worktrees together" tar ((:commit . "b0a5d1bf924d8cadde5bae50b8d9ac131279b828") (:keywords "convenience" "vc") (:url . "https://github.com/purplg/treebundel"))]) (treefactor . [(20200516 1631) ((emacs (26 1)) (dash (2 16 0)) (f (0 20 0)) (org (9 2 6)) (avy (0 5 0))) "Restructure your messy Org documents" tar ((:commit . "75357757022a4399ab772ff0d92065bd114dabe9") (:authors ("Leo Littlebook" . "Leo.Littlebook@gmail.com")) (:maintainers ("Leo Littlebook" . "Leo.Littlebook@gmail.com")) (:maintainer "Leo Littlebook" . "Leo.Littlebook@gmail.com") (:keywords "outlines" "files" "convenience") (:url . "https://github.com/cyberthal/treefactor"))]) - (treemacs . [(20240809 1523) ((emacs (26 1)) (cl-lib (0 5)) (dash (2 11 0)) (s (1 12 0)) (ace-window (0 9 0)) (pfuture (1 7)) (hydra (0 13 2)) (ht (2 2)) (cfrs (1 3 2))) "A tree style file explorer package" tar ((:commit . "568357c8f97a98c40684f74b69981dc2176f90e8") (: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 . [(20240813 1152) ((emacs (26 1)) (cl-lib (0 5)) (dash (2 11 0)) (s (1 12 0)) (ace-window (0 9 0)) (pfuture (1 7)) (hydra (0 13 2)) (ht (2 2)) (cfrs (1 3 2))) "A tree style file explorer package" tar ((:commit . "30919bbda612d7dc862ca23f998c8df66abd3861") (: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-all-the-icons . [(20240131 2042) ((emacs (26 1)) (all-the-icons (4 0 1)) (treemacs (0 0))) "all-the-icons integration for treemacs" tar ((:commit . "bcba09c1581c4bd93ff0217d464aead04f6d26d4") (:authors ("Eric Dallo" . "ercdll1337@gmail.com")) (:maintainers ("Eric Dallo" . "ercdll1337@gmail.com")) (:maintainer "Eric Dallo" . "ercdll1337@gmail.com") (:url . "https://github.com/Alexander-Miller/treemacs"))]) (treemacs-evil . [(20240131 2042) ((emacs (26 1)) (evil (1 2 12)) (treemacs (0 0))) "Evil mode 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-icons-dired . [(20240131 2042) ((treemacs (0 0)) (emacs (26 1))) "Treemacs icons for dired" 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"))]) @@ -5384,6 +5384,7 @@ (uiua-ts-mode . [(20231215 2007) ((emacs (29 1)) (uiua-mode (0 0 5))) "Uiua treesiter mode" tar ((:commit . "1d9b2d4929094e7df7dd23aa1204b4a47c654cc4") (:keywords "languages" "uiua") (:url . "https://github.com/crmsnbleyd/uiua-ts-mode"))]) (ujelly-theme . [(20180214 1624) nil "Ujelly theme for GNU Emacs 24 (deftheme)" tar ((:commit . "bf724ce7806a738d2043544061e5f9bbfc56e674") (:authors ("Mark Tran" . "mark.tran@gmail.com")) (:maintainers ("Mark Tran" . "mark.tran@gmail.com")) (:maintainer "Mark Tran" . "mark.tran@gmail.com") (:url . "http://github.com/marktran/color-theme-ujelly"))]) (ukrainian-holidays . [(20130720 1349) nil "Ukrainian holidays for Emacs calendar." tar ((:commit . "e52b0c92843e9f4d0415a7ba3b8559785497d23d") (:authors ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainers ("Oleh Krehel" . "ohwoeowho@gmail.com")) (:maintainer "Oleh Krehel" . "ohwoeowho@gmail.com") (:url . "https://github.com/abo-abo/ukrainian-holidays"))]) + (ulisp-mode . [(20240807 1000) ((emacs (25 1))) "Major mode for editing and evaluate uLisp" tar ((:commit . "7f52f030e5bf6e98ba9eee75631a6e2b95f90583") (:authors ("DEADBLACKCLOVER" . "deadblackclover@protonmail.com")) (:maintainers ("DEADBLACKCLOVER" . "deadblackclover@protonmail.com")) (:maintainer "DEADBLACKCLOVER" . "deadblackclover@protonmail.com") (:keywords "languages") (:url . "https://codeberg.org/deadblackclover/ulisp-mode"))]) (uml-mode . [(20200129 1147) ((emacs (24 4)) (seq (0))) "Minor mode for ascii uml sequence diagrams" tar ((:commit . "0ef88c74b48b5400d83ab93e3e089bbe45538fd7") (:authors ("Ian Martins" . "ianxm@jhu.edu")) (:maintainers ("Ian Martins" . "ianxm@jhu.edu")) (:maintainer "Ian Martins" . "ianxm@jhu.edu") (:keywords "docs") (:url . "http://github.com/ianxm/emacs-uml"))]) (uncrustify-mode . [(20130707 1359) nil "Minor mode to automatically uncrustify." tar ((:commit . "2c00d5cf2d1868a5955347438746f4dd82b3b9fc") (:authors ("Tabito Ohtani" . "koko1000ban@gmail.com")) (:maintainers ("Tabito Ohtani" . "koko1000ban@gmail.com")) (:maintainer "Tabito Ohtani" . "koko1000ban@gmail.com") (:keywords "uncrustify"))]) (undercover . [(20210602 2119) ((emacs (24)) (dash (2 0 0)) (shut-up (0 3 2))) "Test coverage library for Emacs Lisp" tar ((:commit . "1d3587f1fad66a747688f36636b67b33b73447d3") (:authors ("Sviridov Alexander" . "sviridov.vmi@gmail.com")) (:maintainers ("Sviridov Alexander" . "sviridov.vmi@gmail.com")) (:maintainer "Sviridov Alexander" . "sviridov.vmi@gmail.com") (:keywords "lisp" "tests" "coverage" "tools") (:url . "https://github.com/sviridov/undercover.el"))]) @@ -5767,10 +5768,10 @@ (zig-mode . [(20240416 1636) ((emacs (26 1)) (reformatter (0 6))) "A major mode for the Zig programming language" tar ((:commit . "b4170b747ae4c45d145ff8bcb7fafe095e17b4c6") (:authors ("Andrea Orru" . "andreaorru1991@gmail.com") ("Andrew Kelley" . "superjoe30@gmail.com")) (:maintainers ("Jen-Chieh" . "jcs090218@gmail.com")) (:maintainer "Jen-Chieh" . "jcs090218@gmail.com") (:keywords "zig" "languages") (:url . "https://github.com/zig-lang/zig-mode"))]) (zim-wiki-mode . [(20240613 8) ((emacs (25 1)) (helm-ag (0 58)) (helm-projectile (0 14 0)) (dokuwiki-mode (0 1 1)) (link-hint (0 1)) (pretty-hydra (0 2 2))) "Zim Desktop Wiki edit mode" tar ((:commit . "11e077afbe21f2dd33fe7eae39c2a8345bb0b806") (:authors ("Will Foran" . "willforan+zim-wiki-mode@gmail.com")) (:maintainers ("Will Foran" . "willforan+zim-wiki-mode@gmail.com")) (:maintainer "Will Foran" . "willforan+zim-wiki-mode@gmail.com") (:keywords "outlines") (:url . "https://github.com/WillForan/zim-wiki-mode"))]) (zimports . [(20211011 2059) ((emacs (26 1)) (projectile (2 1 0))) "Reformat python imports with zimports" tar ((:commit . "76cf76bdc871cb0454a6fc555aeb1aa94f1b6e57") (:url . "https://github.com/schmir/zimports.el"))]) - (zk . [(20240704 910) ((emacs (25 1))) "Functions for working with Zettelkasten-style linked notes" tar ((:commit . "225d2628025195283170908e81341e3a8d49b022") (:authors ("Grant Rosson" . "https://github.com/localauthor")) (:maintainers ("Grant Rosson" . "https://github.com/localauthor")) (:maintainer "Grant Rosson" . "https://github.com/localauthor") (:url . "https://github.com/localauthor/zk"))]) - (zk-desktop . [(20240102 1405) ((emacs (27 1)) (zk (0 6)) (zk-index (0 9))) "Desktop environment for zk" tar ((:commit . "82f636c1edda416794c7115757ecded64f22afbf") (:authors ("Grant Rosson" . "https://github.com/localauthor")) (:maintainers ("Grant Rosson" . "https://github.com/localauthor")) (:maintainer "Grant Rosson" . "https://github.com/localauthor") (:url . "https://github.com/localauthor/zk"))]) - (zk-index . [(20240805 2014) ((emacs (27 1)) (zk (0 3))) "Index for zk" tar ((:commit . "cb534a5c4e6525df046efd77b47a49b100d6b48c") (:authors ("Grant Rosson" . "https://github.com/localauthor")) (:maintainers ("Grant Rosson" . "https://github.com/localauthor")) (:maintainer "Grant Rosson" . "https://github.com/localauthor") (:url . "https://github.com/localauthor/zk"))]) - (zk-luhmann . [(20240102 1406) ((emacs (25 1)) (zk (0 4)) (zk-index (0 9))) "Support for Luhmann-style IDs in zk" tar ((:commit . "836943c5f6c6cdb932762385bbdf79e8bcfef3d7") (:authors ("Grant Rosson" . "https://github.com/localauthor")) (:maintainers ("Grant Rosson" . "https://github.com/localauthor")) (:maintainer "Grant Rosson" . "https://github.com/localauthor") (:url . "https://github.com/localauthor/zk-luhmann"))]) + (zk . [(20240812 1148) ((emacs (25 1))) "Functions for working with Zettelkasten-style linked notes" tar ((:commit . "930e6904d8d14bff80a689ffe93c6b1dc15828ca") (:authors ("Grant Rosson" . "https://github.com/localauthor")) (:maintainers ("Grant Rosson" . "https://github.com/localauthor")) (:maintainer "Grant Rosson" . "https://github.com/localauthor") (:url . "https://github.com/localauthor/zk"))]) + (zk-desktop . [(20240812 1148) ((emacs (27 1)) (zk (0 6)) (zk-index (0 9))) "Desktop environment for zk" tar ((:commit . "930e6904d8d14bff80a689ffe93c6b1dc15828ca") (:authors ("Grant Rosson" . "https://github.com/localauthor")) (:maintainers ("Grant Rosson" . "https://github.com/localauthor")) (:maintainer "Grant Rosson" . "https://github.com/localauthor") (:url . "https://github.com/localauthor/zk"))]) + (zk-index . [(20240812 1148) ((emacs (27 1)) (zk (0 7))) "Index for zk" tar ((:commit . "930e6904d8d14bff80a689ffe93c6b1dc15828ca") (:authors ("Grant Rosson" . "https://github.com/localauthor")) (:maintainers ("Grant Rosson" . "https://github.com/localauthor")) (:maintainer "Grant Rosson" . "https://github.com/localauthor") (:url . "https://github.com/localauthor/zk"))]) + (zk-luhmann . [(20240812 1143) ((emacs (25 1)) (zk (0 7)) (zk-index (0 10))) "Support for Luhmann-style IDs in zk" tar ((:commit . "55eef9712984deab320fb1a952c0624af2988128") (:authors ("Grant Rosson" . "https://github.com/localauthor")) (:maintainers ("Grant Rosson" . "https://github.com/localauthor")) (:maintainer "Grant Rosson" . "https://github.com/localauthor") (:url . "https://github.com/localauthor/zk-luhmann"))]) (zlc . [(20151011 157) nil "Provides zsh like completion system to Emacs" tar ((:commit . "4dd2ba267ecdeac845a7cbb3147294ee7daa25f4") (:authors ("mooz" . "stillpedant@gmail.com")) (:maintainers ("mooz" . "stillpedant@gmail.com")) (:maintainer "mooz" . "stillpedant@gmail.com") (:keywords "matching" "convenience"))]) (zmq . [(20240716 2000) ((cl-lib (0 5)) (emacs (26))) "ZMQ bindings in Emacs-Lisp" tar ((:commit . "1d9d5a3b46cfd1a90a45ff777f200eb4d5d6fd9e") (:authors ("Nathaniel Nicandro" . "nathanielnicandro@gmail.com")) (:maintainers ("Nathaniel Nicandro" . "nathanielnicandro@gmail.com")) (:maintainer "Nathaniel Nicandro" . "nathanielnicandro@gmail.com") (:keywords "comm") (:url . "https://github.com/nnicandro/emacs-zmq"))]) (znc . [(20210803 159) ((cl-lib (0 2))) "ZNC + ERC" tar ((:commit . "2605f78e37a8a759067dc14fa25a82824ba1bacc") (:url . "https://github.com/sshirokov/ZNC.el"))]) diff --git a/emacs/elpa/archives/nongnu/archive-contents b/emacs/elpa/archives/nongnu/archive-contents @@ -384,7 +384,7 @@ ("Campbell Barton" . "ideasman42@gmail.com")) (:commit . "83d94733fd8ed64f2ba40f4e1df7ecbfe8260e51"))]) (d-mode . - [(202405290611) + [(202408131340) ((emacs (25 1))) "D Programming Language major mode for (X)Emacs" tar @@ -393,7 +393,7 @@ ("Russel Winder" . "russel@winder.org.uk") ("Vladimir Panteleev" . "vladimir@thecybershadow.net")) (:url . "https://elpa.nongnu.org/nongnu/d-mode.html") - (:commit . "7d280251de13c0042c0846ff4eaa5b1937fcbe01"))]) + (:commit . "688f5af819e461badfd6fda642937153b4946813"))]) (dart-mode . [(1 0 7) ((emacs @@ -951,9 +951,9 @@ ("András Simonyi" . "andras.simonyi@gmail.com")) (:commit . "9e7ed54e5629f759660569bc7efc3d75dbabbc5f"))]) (geiser-guile . - [(0 28 1) + [(0 28 2) ((emacs - (25 1)) + (26 1)) (transient (0 3)) (geiser @@ -964,7 +964,7 @@ (:maintainer "Jose Antonio Ortega Ruiz" . "(jao@gnu.org)") (:authors ("Jose Antonio Ortega Ruiz" . "(jao@gnu.org)")) - (:commit . "f57bfd5039ce158de95d2ef2933e64fb16def4f6"))]) + (:commit . "5a856c2982030ff77e2d151ead4fcd991512f362"))]) (geiser-kawa . [(0 0 1) ((emacs @@ -1988,7 +1988,7 @@ ("David Christiansen" . "david@davidchristiansen.dk")) (:commit . "1edda80e2e32b72e77f4f16ae5b83c312c68ee95"))]) (racket-mode . - [(1 0 20240808 93008) + [(1 0 20240810 92632) ((emacs (25 1))) "Racket editing, REPL, and more" tar diff --git a/emacs/elpa/archives/nongnu/archive-contents.signed b/emacs/elpa/archives/nongnu/archive-contents.signed @@ -1 +1 @@ -Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) <elpasign@elpa.gnu.org> (trust undefined) created at 2024-08-10T09:10:05+0000 using EDDSA -\ No newline at end of file +Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) <elpasign@elpa.gnu.org> (trust undefined) created at 2024-08-14T09:10:04+0000 using EDDSA +\ No newline at end of file diff --git a/emacs/elpa/consult-20240725.508/consult-org.elc b/emacs/elpa/consult-20240725.508/consult-org.elc Binary files differ. diff --git a/emacs/elpa/consult-20240725.508/consult-pkg.el b/emacs/elpa/consult-20240725.508/consult-pkg.el @@ -1,13 +0,0 @@ -(define-package "consult" "20240725.508" "Consulting completing-read" - '((emacs "27.1") - (compat "30")) - :commit "4889458dccf842ab6223099f8a73ff8b147e9459" :maintainers - '(("Daniel Mendler" . "mail@daniel-mendler.de")) - :maintainer - '("Daniel Mendler" . "mail@daniel-mendler.de") - :keywords - '("matching" "files" "completion") - :url "https://github.com/minad/consult") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/consult-20240725.508/consult.el b/emacs/elpa/consult-20240725.508/consult.el @@ -1,5272 +0,0 @@ -;;; consult.el --- Consulting completing-read -*- lexical-binding: t -*- - -;; Copyright (C) 2021-2024 Free Software Foundation, Inc. - -;; Author: Daniel Mendler and Consult contributors -;; Maintainer: Daniel Mendler <mail@daniel-mendler.de> -;; Created: 2020 -;; Version: 1.8 -;; Package-Requires: ((emacs "27.1") (compat "30")) -;; Homepage: https://github.com/minad/consult -;; Keywords: matching, files, 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: - -;; Consult implements a set of `consult-<thing>' commands, which aim to -;; improve the way you use Emacs. The commands are founded on -;; `completing-read', which selects from a list of candidate strings. -;; Consult provides an enhanced buffer switcher `consult-buffer' and -;; search and navigation commands like `consult-imenu' and -;; `consult-line'. Searching through multiple files is supported by the -;; asynchronous `consult-grep' command. Many Consult commands support -;; previewing candidates. If a candidate is selected in the completion -;; view, the buffer shows the candidate immediately. - -;; The Consult commands are compatible with multiple completion systems -;; based on the Emacs `completing-read' API, including the default -;; completion system, Vertico, Mct and Icomplete. - -;; See the README for an overview of the available Consult commands and -;; the documentation of the configuration and installation of the -;; package. - -;; The full list of contributors can be found in the acknowledgments -;; section of the README. - -;;; Code: - -(eval-when-compile - (require 'cl-lib) - (require 'subr-x)) -(require 'compat) -(require 'bookmark) - -(defgroup consult nil - "Consulting `completing-read'." - :link '(info-link :tag "Info Manual" "(consult)") - :link '(url-link :tag "Homepage" "https://github.com/minad/consult") - :link '(emacs-library-link :tag "Library Source" "consult.el") - :group 'files - :group 'outlines - :group 'minibuffer - :prefix "consult-") - -;;;; Customization - -(defcustom consult-narrow-key nil - "Prefix key for narrowing during completion. - -Good choices for this key are \"<\" and \"C-+\" for example. The -key must be a string accepted by `key-valid-p'." - :type '(choice key (const :tag "None" nil))) - -(defcustom consult-widen-key nil - "Key used for widening during completion. - -If this key is unset, defaults to twice the `consult-narrow-key'. -The key must be a string accepted by `key-valid-p'." - :type '(choice key (const :tag "None" nil))) - -(defcustom consult-project-function - #'consult--default-project-function - "Function which returns project root directory. -The function takes one boolean argument MAY-PROMPT. If -MAY-PROMPT is non-nil, the function may ask the prompt the user -for a project directory. The root directory is used by -`consult-buffer' and `consult-grep'." - :type `(choice - (const :tag "Default project function" ,#'consult--default-project-function) - (function :tag "Custom function") - (const :tag "No project integration" nil))) - -(defcustom consult-async-refresh-delay 0.2 - "Refreshing delay of the completion UI for asynchronous commands. - -The completion UI is only updated every -`consult-async-refresh-delay' seconds. This applies to -asynchronous commands like for example `consult-grep'." - :type '(float :tag "Delay in seconds")) - -(defcustom consult-async-input-throttle 0.5 - "Input throttle for asynchronous commands. - -The asynchronous process is started only every -`consult-async-input-throttle' seconds. This applies to asynchronous -commands, e.g., `consult-grep'." - :type '(float :tag "Delay in seconds")) - -(defcustom consult-async-input-debounce 0.2 - "Input debounce for asynchronous commands. - -The asynchronous process is started only when there has not been new -input for `consult-async-input-debounce' seconds. This applies to -asynchronous commands, e.g., `consult-grep'." - :type '(float :tag "Delay in seconds")) - -(defcustom consult-async-min-input 3 - "Minimum number of characters needed, before asynchronous process is called. - -This applies to asynchronous commands, e.g., `consult-grep'." - :type '(natnum :tag "Number of characters")) - -(defcustom consult-async-split-style 'perl - "Async splitting style, see `consult-async-split-styles-alist'." - :type '(choice (const :tag "No splitting" nil) - (const :tag "Comma" comma) - (const :tag "Semicolon" semicolon) - (const :tag "Perl" perl))) - -(defcustom consult-async-split-styles-alist - `((nil :function ,#'consult--split-nil) - (comma :separator ?, :function ,#'consult--split-separator) - (semicolon :separator ?\; :function ,#'consult--split-separator) - (perl :initial "#" :function ,#'consult--split-perl)) - "Async splitting styles." - :type '(alist :key-type symbol :value-type plist)) - -(defcustom consult-mode-histories - '((eshell-mode eshell-history-ring eshell-history-index eshell-bol) - (comint-mode comint-input-ring comint-input-ring-index comint-bol) - (term-mode term-input-ring term-input-ring-index term-bol)) - "Alist of mode histories (mode history index bol). -The histories can be rings or lists. Index, if provided, is a -variable to set to the index of the selection within the ring or -list. Bol, if provided is a function which jumps to the beginning -of the line after the prompt." - :type '(alist :key-type symbol - :value-type (group :tag "Include Index" - (symbol :tag "List/Ring") - (symbol :tag "Index Variable") - (symbol :tag "Bol Function")))) - -(defcustom consult-themes nil - "List of themes (symbols or regexps) to be presented for selection. -nil shows all `custom-available-themes'." - :type '(repeat (choice symbol regexp))) - -(defcustom consult-after-jump-hook (list #'recenter) - "Function called after jumping to a location. - -Commonly used functions for this hook are `recenter' and -`reposition-window'. You may want to add a function which pulses -the current line, e.g., `pulse-momentary-highlight-one-line' is -supported on Emacs 28 and newer. The hook called during preview -and for the jump after selection." - :type 'hook) - -(defcustom consult-line-start-from-top nil - "Start search from the top if non-nil. -Otherwise start the search at the current line and wrap around." - :type 'boolean) - -(defcustom consult-point-placement 'match-beginning - "Where to leave point when jumping to a match. -This setting affects the command `consult-line' and the `consult-grep' variants." - :type '(choice (const :tag "Beginning of the line" line-beginning) - (const :tag "Beginning of the match" match-beginning) - (const :tag "End of the match" match-end))) - -(defcustom consult-line-numbers-widen t - "Show absolute line numbers when narrowing is active. - -See also `display-line-numbers-widen'." - :type 'boolean) - -(defcustom consult-goto-line-numbers t - "Show line numbers for `consult-goto-line'." - :type 'boolean) - -(defcustom consult-fontify-preserve t - "Preserve fontification for line-based commands." - :type 'boolean) - -(defcustom consult-fontify-max-size 1048576 - "Buffers larger than this byte limit are not fontified. - -This is necessary in order to prevent a large startup time -for navigation commands like `consult-line'." - :type '(natnum :tag "Buffer size in bytes")) - -(defcustom consult-buffer-filter - '("\\` " - "\\`\\*Completions\\*\\'" - "\\`\\*Flymake log\\*\\'" - "\\`\\*Semantic SymRef\\*\\'" - "\\`\\*vc\\*\\'" - "\\`newsrc-dribble\\'" ;; Gnus - "\\`\\*tramp/.*\\*\\'") - "Filter regexps for `consult-buffer'. - -The default setting is to filter ephemeral buffer names beginning -with a space character, the *Completions* buffer and a few log -buffers. The regular expressions are matched case sensitively." - :type '(repeat regexp)) - -(defcustom consult-buffer-sources - '(consult--source-hidden-buffer - consult--source-modified-buffer - consult--source-buffer - consult--source-recent-file - consult--source-file-register - consult--source-bookmark - consult--source-project-buffer-hidden - consult--source-project-recent-file-hidden) - "Sources used by `consult-buffer'. -See also `consult-project-buffer-sources'. -See `consult--multi' for a description of the source data structure." - :type '(repeat symbol)) - -(defcustom consult-project-buffer-sources - '(consult--source-project-buffer - consult--source-project-recent-file) - "Sources used by `consult-project-buffer'. -See also `consult-buffer-sources'. -See `consult--multi' for a description of the source data structure." - :type '(repeat symbol)) - -(defcustom consult-mode-command-filter - '(;; Filter commands - "-mode\\'" "--" - ;; Filter whole features - simple mwheel time so-long recentf tab-bar tab-line) - "Filter commands for `consult-mode-command'." - :type '(repeat (choice symbol regexp))) - -(defcustom consult-grep-max-columns 300 - "Maximal number of columns of grep output." - :type 'natnum) - -(defconst consult--grep-match-regexp - "\\`\\(?:\\./\\)?\\([^\n\0]+\\)\0\\([0-9]+\\)\\([-:\0]\\)" - "Regexp used to match file and line of grep output.") - -(defcustom consult-grep-args - '("grep" (consult--grep-exclude-args) - "--null --line-buffered --color=never --ignore-case\ - --with-filename --line-number -I -r") - "Command line arguments for grep, see `consult-grep'. -The dynamically computed arguments are appended. -Can be either a string, or a list of strings or expressions." - :type '(choice string (repeat (choice string sexp)))) - -(defcustom consult-git-grep-args - "git --no-pager grep --null --color=never --ignore-case\ - --extended-regexp --line-number -I" - "Command line arguments for git-grep, see `consult-git-grep'. -The dynamically computed arguments are appended. -Can be either a string, or a list of strings or expressions." - :type '(choice string (repeat (choice string sexp)))) - -(defcustom consult-ripgrep-args - "rg --null --line-buffered --color=never --max-columns=1000 --path-separator /\ - --smart-case --no-heading --with-filename --line-number --search-zip" - "Command line arguments for ripgrep, see `consult-ripgrep'. -The dynamically computed arguments are appended. -Can be either a string, or a list of strings or expressions." - :type '(choice string (repeat (choice string sexp)))) - -(defcustom consult-find-args - "find . -not ( -path */.[A-Za-z]* -prune )" - "Command line arguments for find, see `consult-find'. -The dynamically computed arguments are appended. -Can be either a string, or a list of strings or expressions." - :type '(choice string (repeat (choice string sexp)))) - -(defcustom consult-fd-args - '((if (executable-find "fdfind" 'remote) "fdfind" "fd") - "--full-path --color=never") - "Command line arguments for fd, see `consult-fd'. -The dynamically computed arguments are appended. -Can be either a string, or a list of strings or expressions." - :type '(choice string (repeat (choice string sexp)))) - -(defcustom consult-locate-args - "locate --ignore-case" ;; --existing not supported by Debian plocate - "Command line arguments for locate, see `consult-locate'. -The dynamically computed arguments are appended. -Can be either a string, or a list of strings or expressions." - :type '(choice string (repeat (choice string sexp)))) - -(defcustom consult-man-args - "man -k" - "Command line arguments for man, see `consult-man'. -The dynamically computed arguments are appended. -Can be either a string, or a list of strings or expressions." - :type '(choice string (repeat (choice string sexp)))) - -(defcustom consult-preview-key 'any - "Preview trigger keys, can be nil, `any', a single key or a list of keys. -Debouncing can be specified via the `:debounce' attribute. The -individual keys must be strings accepted by `key-valid-p'." - :type '(choice (const :tag "Any key" any) - (list :tag "Debounced" - (const :debounce) - (float :tag "Seconds" 0.1) - (const any)) - (const :tag "No preview" nil) - (key :tag "Key") - (repeat :tag "List of keys" key))) - -(defcustom consult-preview-partial-size 1048576 - "Files larger than this byte limit are previewed partially." - :type '(natnum :tag "File size in bytes")) - -(defcustom consult-preview-partial-chunk 102400 - "Partial preview chunk size in bytes. -If a file is larger than `consult-preview-partial-size' only the -chunk from the beginning of the file is previewed." - :type '(natnum :tag "Chunk size in bytes")) - -(defcustom consult-preview-max-count 10 - "Number of file buffers to keep open temporarily during preview." - :type '(natnum :tag "Number of buffers")) - -(defcustom consult-preview-excluded-buffers nil - "Buffers excluded from preview. -The value should conform to the predicate format demanded by the -function `buffer-match-p'." - :type 'sexp) - -(defcustom consult-preview-excluded-files - '("\\`/[^/|:]+:") ;; Do not preview remote files - "List of regexps matched against names of files, which are not previewed." - :type '(repeat regexp)) - -(defcustom consult-preview-allowed-hooks - '(global-font-lock-mode - save-place-find-file-hook) - "List of hooks, which should be executed during file preview. -This variable applies to `find-file-hook', `change-major-mode-hook' and -mode hooks, e.g., `prog-mode-hook'." - :type '(repeat symbol)) - -(defcustom consult-preview-variables - '((inhibit-message . t) - (enable-dir-local-variables . nil) - (enable-local-variables . :safe) - (non-essential . t) - (delay-mode-hooks . t)) - "Variables which are bound for file preview." - :type '(alist :key-type symbol)) - -(defcustom consult-bookmark-narrow - `((?f "File" bookmark-default-handler) - (?h "Help" help-bookmark-jump Info-bookmark-jump - Man-bookmark-jump woman-bookmark-jump) - (?p "Picture" image-bookmark-jump) - (?d "Docview" doc-view-bookmark-jump) - (?m "Mail" gnus-summary-bookmark-jump) - (?s "Eshell" eshell-bookmark-jump) - (?w "Web" eww-bookmark-jump xwidget-webkit-bookmark-jump-handler) - (?v "VC Directory" vc-dir-bookmark-jump) - (nil "Other")) - "Bookmark narrowing configuration. - -Each element of the list must have the form (char name handlers...)." - :type '(alist :key-type character :value-type (cons string (repeat function)))) - -(defcustom consult-yank-rotate - (if (boundp 'yank-from-kill-ring-rotate) - yank-from-kill-ring-rotate - t) - "Rotate the `kill-ring' in the `consult-yank' commands." - :type 'boolean) - -;;;; Faces - -(defgroup consult-faces nil - "Faces used by Consult." - :group 'consult - :group 'faces) - -(defface consult-preview-line - '((t :inherit consult-preview-insertion :extend t)) - "Face used for line previews.") - -(defface consult-highlight-match - '((t :inherit match)) - "Face used to highlight matches in the completion candidates. -Used for example by `consult-grep'.") - -(defface consult-highlight-mark - '((t :inherit consult-highlight-match)) - "Face used for mark positions in completion candidates. -Used for example by `consult-mark'. The face should be different -than the `cursor' face to avoid confusion.") - -(defface consult-preview-match - '((t :inherit isearch)) - "Face used for match previews, e.g., in `consult-line'.") - -(defface consult-preview-insertion - '((t :inherit region)) - "Face used for previews of text to be inserted. -Used by `consult-completion-in-region', `consult-yank' and `consult-history'.") - -(defface consult-narrow-indicator - '((t :inherit warning)) - "Face used for the narrowing indicator.") - -(defface consult-async-running - '((t :inherit consult-narrow-indicator)) - "Face used if asynchronous process is running.") - -(defface consult-async-finished - '((t :inherit success)) - "Face used if asynchronous process has finished.") - -(defface consult-async-failed - '((t :inherit error)) - "Face used if asynchronous process has failed.") - -(defface consult-async-split - '((t :inherit font-lock-negation-char-face)) - "Face used to highlight punctuation character.") - -(defface consult-help - '((t :inherit shadow)) - "Face used to highlight help, e.g., in `consult-register-store'.") - -(defface consult-key - '((t :inherit font-lock-keyword-face)) - "Face used to highlight keys, e.g., in `consult-register'.") - -(defface consult-line-number - '((t :inherit consult-key)) - "Face used to highlight location line in `consult-global-mark'.") - -(defface consult-file - '((t :inherit font-lock-function-name-face)) - "Face used to highlight files in `consult-buffer'.") - -(defface consult-grep-context - '((t :inherit shadow)) - "Face used to highlight grep context in `consult-grep'.") - -(defface consult-bookmark - '((t :inherit font-lock-constant-face)) - "Face used to highlight bookmarks in `consult-buffer'.") - -(defface consult-buffer - '((t)) - "Face used to highlight buffers in `consult-buffer'.") - -(defface consult-line-number-prefix - '((t :inherit line-number)) - "Face used to highlight line number prefixes.") - -(defface consult-line-number-wrapped - '((t :inherit consult-line-number-prefix :inherit font-lock-warning-face)) - "Face used to highlight line number prefixes after wrap around.") - -(defface consult-separator - '((((class color) (min-colors 88) (background light)) - :foreground "#ccc") - (((class color) (min-colors 88) (background dark)) - :foreground "#333")) - "Face used for thin line separators in `consult-register-window'.") - -;;;; Input history variables - -(defvar consult--path-history nil) -(defvar consult--grep-history nil) -(defvar consult--find-history nil) -(defvar consult--man-history nil) -(defvar consult--line-history nil) -(defvar consult--line-multi-history nil) -(defvar consult--theme-history nil) -(defvar consult--minor-mode-menu-history nil) -(defvar consult--buffer-history nil) - -;;;; Internal variables - -(defvar consult--regexp-compiler - #'consult--default-regexp-compiler - "Regular expression compiler used by `consult-grep' and other commands. -The function must return a list of regular expressions and a highlighter -function.") - -(defvar consult--customize-alist - ;; Disable preview in frames, since `consult--jump-preview' does not properly - ;; clean up. See gh:minad/consult#593. This issue should better be fixed in - ;; `consult--jump-preview'. - `((,#'consult-buffer-other-frame :preview-key nil) - (,#'consult-buffer-other-tab :preview-key nil)) - "Command configuration alist for fine-grained configuration. - -Each element of the list must have the form (command-name plist...). The -options set here will be evaluated and passed to `consult--read', when -called from the corresponding command. Note that the options depend on -the private `consult--read' API and should not be considered as stable -as the public API.") - -(defvar consult--buffer-display #'switch-to-buffer - "Buffer display function.") - -(defvar consult--completion-candidate-hook - (list #'consult--default-completion-minibuffer-candidate - #'consult--default-completion-list-candidate) - "Get candidate from completion system.") - -(defvar consult--completion-refresh-hook nil - "Refresh completion system.") - -(defvar-local consult--preview-function nil - "Minibuffer-local variable which exposes the current preview function. -This function can be called by custom completion systems from -outside the minibuffer.") - -(defvar consult--annotate-align-step 10 - "Round candidate width.") - -(defvar consult--annotate-align-width 0 - "Maximum candidate width used for annotation alignment.") - -(defconst consult--tofu-char #x200000 - "Special character used to encode line prefixes for disambiguation. -We use invalid characters outside the Unicode range.") - -(defconst consult--tofu-range #x100000 - "Special character range.") - -(defvar-local consult--narrow nil - "Current narrowing key.") - -(defvar-local consult--narrow-keys nil - "Narrowing prefixes of the current completion.") - -(defvar-local consult--narrow-predicate nil - "Narrowing predicate of the current completion.") - -(defvar-local consult--narrow-overlay nil - "Narrowing indicator overlay.") - -(defvar consult--gc-threshold (* 64 1024 1024) - "Large GC threshold for temporary increase.") - -(defvar consult--gc-percentage 0.5 - "Large GC percentage for temporary increase.") - -(defvar consult--process-chunk (* 1024 1024) - "Increase process output chunk size.") - -(defvar consult--async-log - " *consult-async*" - "Buffer for async logging output used by `consult--async-process'.") - -(defvar-local consult--focus-lines-overlays nil - "Overlays used by `consult-focus-lines'.") - -(defvar-local consult--org-fold-regions nil - "Stored regions for the org-fold API.") - -;;;; Miscellaneous helper functions - -(defun consult--key-parse (key) - "Parse KEY or signal error if invalid." - (unless (key-valid-p key) - (error "%S is not a valid key definition; see `key-valid-p'" key)) - (key-parse key)) - -(defun consult--in-buffer (fun &optional buffer) - "Ensure that FUN is executed inside BUFFER." - (unless buffer (setq buffer (current-buffer))) - (lambda (&rest args) - (with-current-buffer buffer - (apply fun args)))) - -(defun consult--completion-table-in-buffer (table &optional buffer) - "Ensure that completion TABLE is executed inside BUFFER." - (if (functionp table) - (consult--in-buffer - (lambda (str pred action) - (let ((result (funcall table str pred action))) - (pcase action - ('metadata - (setq result - (mapcar - (lambda (x) - (if (and (string-suffix-p "-function" (symbol-name (car-safe x))) (cdr x)) - (cons (car x) (consult--in-buffer (cdr x))) - x)) - result))) - ((and 'completion--unquote (guard (functionp (cadr result)))) - (cl-callf consult--in-buffer (cadr result) buffer) - (cl-callf consult--in-buffer (cadddr result) buffer))) - result)) - buffer) - table)) - -(defun consult--build-args (arg) - "Return ARG as a flat list of split strings. - -Turn ARG into a list, and for each element either: -- split it if it a string. -- eval it if it is an expression." - (seq-mapcat (lambda (x) - (if (stringp x) - (split-string-and-unquote x) - (ensure-list (eval x 'lexical)))) - (ensure-list arg))) - -(defun consult--command-split (str) - "Return command argument and options list given input STR." - (save-match-data - (let ((opts (when (string-match " +--\\( +\\|\\'\\)" str) - (prog1 (substring str (match-end 0)) - (setq str (substring str 0 (match-beginning 0))))))) - ;; split-string-and-unquote fails if the quotes are invalid. Ignore it. - (cons str (and opts (ignore-errors (split-string-and-unquote opts))))))) - -(defmacro consult--keep! (list form) - "Evaluate FORM for every element of LIST and keep the non-nil results." - (declare (indent 1)) - (cl-with-gensyms (head prev result) - `(let* ((,head (cons nil ,list)) - (,prev ,head)) - (while (cdr ,prev) - (if-let (,result (let ((it (cadr ,prev))) ,form)) - (progn - (pop ,prev) - (setcar ,prev ,result)) - (setcdr ,prev (cddr ,prev)))) - (setq ,list (cdr ,head)) - nil))) - -;; Upstream bug#46326, Consult issue gh:minad/consult#193. -(defmacro consult--minibuffer-with-setup-hook (fun &rest body) - "Variant of `minibuffer-with-setup-hook' using a symbol and `fset'. - -This macro is only needed to prevent memory leaking issues with -the upstream `minibuffer-with-setup-hook' macro. -FUN is the hook function and BODY opens the minibuffer." - (declare (indent 1) (debug t)) - (let ((hook (gensym "hook")) - (append)) - (when (eq (car-safe fun) :append) - (setq append '(t) fun (cadr fun))) - `(let ((,hook (make-symbol "consult--minibuffer-setup-hook"))) - (fset ,hook (lambda () - (remove-hook 'minibuffer-setup-hook ,hook) - (funcall ,fun))) - (unwind-protect - (progn - (add-hook 'minibuffer-setup-hook ,hook ,@append) - ,@body) - (remove-hook 'minibuffer-setup-hook ,hook))))) - -(defun consult--completion-filter (pattern cands category _highlight) - "Filter CANDS with PATTERN. - -CATEGORY is the completion category, used to find the completion style via -`completion-category-defaults' and `completion-category-overrides'. -HIGHLIGHT must be non-nil if the resulting strings should be highlighted." - ;; completion-all-completions returns an improper list - ;; where the last link is not necessarily nil. - (nconc (completion-all-completions pattern cands nil (length pattern) - `(metadata (category . ,category))) - nil)) - -(defun consult--completion-filter-complement (pattern cands category _highlight) - "Filter CANDS with complement of PATTERN. -See `consult--completion-filter' for the arguments CATEGORY and HIGHLIGHT." - (let ((ht (consult--string-hash (consult--completion-filter pattern cands category nil)))) - (seq-remove (lambda (x) (gethash x ht)) cands))) - -(defun consult--completion-filter-dispatch (pattern cands category highlight) - "Filter CANDS with PATTERN with optional complement. -Either using `consult--completion-filter' or -`consult--completion-filter-complement', depending on if the pattern starts -with a bang. See `consult--completion-filter' for the arguments CATEGORY and -HIGHLIGHT." - (cond - ((string-match-p "\\`!? ?\\'" pattern) cands) ;; empty pattern - ((string-prefix-p "! " pattern) (consult--completion-filter-complement - (substring pattern 2) cands category nil)) - (t (consult--completion-filter pattern cands category highlight)))) - -(defmacro consult--each-line (beg end &rest body) - "Iterate over each line. - -The line beginning/ending BEG/END is bound in BODY." - (declare (indent 2)) - (cl-with-gensyms (max) - `(save-excursion - (let ((,beg (point-min)) (,max (point-max)) ,end) - (while (< ,beg ,max) - (goto-char ,beg) - (setq ,end (pos-eol)) - ,@body - (setq ,beg (1+ ,end))))))) - -(defun consult--display-width (string) - "Compute width of STRING taking display and invisible properties into account." - (let ((pos 0) (width 0) (end (length string))) - (while (< pos end) - (let ((nextd (next-single-property-change pos 'display string end)) - (display (get-text-property pos 'display string))) - (if (stringp display) - (setq width (+ width (string-width display)) - pos nextd) - (while (< pos nextd) - (let ((nexti (next-single-property-change pos 'invisible string nextd))) - (unless (get-text-property pos 'invisible string) - (setq width (+ width (compat-call string-width string pos nexti)))) - (setq pos nexti)))))) - width)) - -(defun consult--string-hash (strings) - "Create hash table from STRINGS." - (let ((ht (make-hash-table :test #'equal :size (length strings)))) - (dolist (str strings) - (puthash str t ht)) - ht)) - -(defmacro consult--local-let (binds &rest body) - "Buffer local let BINDS of dynamic variables in BODY." - (declare (indent 1)) - (let ((buffer (gensym "buffer")) - (local (mapcar (lambda (x) (cons (gensym "local") (car x))) binds))) - `(let ((,buffer (current-buffer)) - ,@(mapcar (lambda (x) `(,(car x) (local-variable-p ',(cdr x)))) local)) - (unwind-protect - (progn - ,@(mapcar (lambda (x) `(make-local-variable ',(car x))) binds) - (let (,@binds) - ,@body)) - (when (buffer-live-p ,buffer) - (with-current-buffer ,buffer - ,@(mapcar (lambda (x) - `(unless ,(car x) - (kill-local-variable ',(cdr x)))) - local))))))) - -(defvar consult--fast-abbreviate-file-name nil) -(defun consult--fast-abbreviate-file-name (name) - "Return abbreviate file NAME. -This function is a pure variant of `abbreviate-file-name', which -does not access the file system. This is important if we require -that the operation is fast, even for remote paths or paths on -network file systems." - (save-match-data - (let (case-fold-search) ;; Assume that file system is case sensitive. - (setq name (directory-abbrev-apply name)) - (if (string-match (with-memoization consult--fast-abbreviate-file-name - (directory-abbrev-make-regexp (expand-file-name "~"))) - name) - (concat "~" (substring name (match-beginning 1))) - name)))) - -(defun consult--left-truncate-file (file) - "Return abbreviated file name of FILE for use in `completing-read' prompt." - (save-match-data - (let ((afile (abbreviate-file-name file))) - (if (string-match "/\\([^/]+\\)/\\([^/]+/?\\)\\'" afile) - (propertize (format "…/%s/%s" (match-string 1 afile) (match-string 2 afile)) - 'help-echo afile) - afile)))) - -(defun consult--directory-prompt (prompt dir) - "Return prompt, paths and default directory. - -PROMPT is the prompt prefix. The directory is appended to the -prompt prefix. For projects only the project name is shown. The -`default-directory' is not shown. Other directories are -abbreviated and only the last two path components are shown. - -If DIR is a string, it is returned as default directory. If DIR -is a list of strings, the list is returned as search paths. If -DIR is nil the `consult-project-function' is tried to retrieve -the default directory. If no project is found the -`default-directory' is returned as is. Otherwise the user is -asked for the directories or files to search via -`completing-read-multiple'." - (let* ((paths nil) - (dir - (pcase dir - ((pred stringp) dir) - ('nil (or (consult--project-root) default-directory)) - (_ - (pcase (if (stringp (car-safe dir)) - dir - ;; Preserve this-command across `completing-read-multiple' call, - ;; such that `consult-customize' continues to work. - (let ((this-command this-command) - (def (abbreviate-file-name default-directory)) - ;; TODO: `minibuffer-completing-file-name' is - ;; mostly deprecated, but still in use. Packages - ;; should instead use the completion metadata. - (minibuffer-completing-file-name t) - (ignore-case read-file-name-completion-ignore-case)) - (consult--minibuffer-with-setup-hook - (lambda () - (setq-local completion-ignore-case ignore-case) - (set-syntax-table minibuffer-local-filename-syntax)) - (completing-read-multiple "Directories or files: " - #'completion-file-name-table - nil t def 'consult--path-history def)))) - ((and `(,p) (guard (file-directory-p p))) p) - (ps (setq paths (mapcar (lambda (p) - (file-relative-name (expand-file-name p))) - ps)) - default-directory))))) - (edir (file-name-as-directory (expand-file-name dir))) - (pdir (let ((default-directory edir)) - ;; Bind default-directory in order to find the project - (consult--project-root)))) - (list - (format "%s (%s): " prompt - (pcase paths - (`(,p) (consult--left-truncate-file p)) - (`(,p . ,_) - (format "%d paths, %s, …" (length paths) (consult--left-truncate-file p))) - ((guard (equal edir pdir)) (concat "Project " (consult--project-name pdir))) - (_ (consult--left-truncate-file edir)))) - (or paths '(".")) - edir))) - -(defun consult--default-project-function (may-prompt) - "Return project root directory. -When no project is found and MAY-PROMPT is non-nil ask the user." - (when-let (proj (project-current may-prompt)) - (cond - ((fboundp 'project-root) (project-root proj)) - ((fboundp 'project-roots) (car (project-roots proj)))))) - -(defun consult--project-root (&optional may-prompt) - "Return project root as absolute path. -When no project is found and MAY-PROMPT is non-nil ask the user." - ;; Preserve this-command across project selection, - ;; such that `consult-customize' continues to work. - (let ((this-command this-command)) - (when-let (root (and consult-project-function - (funcall consult-project-function may-prompt))) - (expand-file-name root)))) - -(defun consult--project-name (dir) - "Return the project name for DIR." - (if (string-match "/\\([^/]+\\)/\\'" dir) - (propertize (match-string 1 dir) 'help-echo (abbreviate-file-name dir)) - dir)) - -(defun consult--format-file-line-match (file line match) - "Format string FILE:LINE:MATCH with faces." - (setq line (number-to-string line) - match (concat file ":" line ":" match) - file (length file)) - (put-text-property 0 file 'face 'consult-file match) - (put-text-property (1+ file) (+ 1 file (length line)) 'face 'consult-line-number match) - match) - -(defun consult--make-overlay (beg end &rest props) - "Make consult overlay between BEG and END with PROPS." - (let ((ov (make-overlay beg end))) - (while props - (overlay-put ov (car props) (cadr props)) - (setq props (cddr props))) - ov)) - -(defun consult--remove-dups (list) - "Remove duplicate strings from LIST." - (delete-dups (copy-sequence list))) - -(defsubst consult--in-range-p (pos) - "Return t if position POS lies in range `point-min' to `point-max'." - (<= (point-min) pos (point-max))) - -(defun consult--completion-window-p () - "Return non-nil if the selected window belongs to the completion UI." - (or (eq (selected-window) (active-minibuffer-window)) - (eq #'completion-list-mode (buffer-local-value 'major-mode (window-buffer))))) - -(defun consult--original-window () - "Return window which was just selected just before the minibuffer was entered. -In contrast to `minibuffer-selected-window' never return nil and -always return an appropriate non-minibuffer window." - (or (minibuffer-selected-window) - (if (window-minibuffer-p (selected-window)) - (next-window) - (selected-window)))) - -(defun consult--forbid-minibuffer () - "Raise an error if executed from the minibuffer." - (when (minibufferp) - (user-error "`%s' called inside the minibuffer" this-command))) - -(defun consult--require-minibuffer () - "Raise an error if executed outside the minibuffer." - (unless (minibufferp) - (user-error "`%s' must be called inside the minibuffer" this-command))) - -(defun consult--fontify-all () - "Ensure that the whole buffer is fontified." - ;; Font-locking is lazy, i.e., if a line has not been looked at yet, the line - ;; is not font-locked. We would observe this if consulting an unfontified - ;; line. Therefore we have to enforce font-locking now, which is slow. In - ;; order to prevent is hang-up we check the buffer size against - ;; `consult-fontify-max-size'. - (when (and consult-fontify-preserve jit-lock-mode - (< (buffer-size) consult-fontify-max-size)) - (jit-lock-fontify-now))) - -(defun consult--fontify-region (start end) - "Ensure that region between START and END is fontified." - (when (and consult-fontify-preserve jit-lock-mode) - (jit-lock-fontify-now start end))) - -(defmacro consult--with-increased-gc (&rest body) - "Temporarily increase the GC limit in BODY to optimize for throughput." - (cl-with-gensyms (overwrite) - `(let* ((,overwrite (> consult--gc-threshold gc-cons-threshold)) - (gc-cons-threshold (if ,overwrite consult--gc-threshold gc-cons-threshold)) - (gc-cons-percentage (if ,overwrite consult--gc-percentage gc-cons-percentage))) - ,@body))) - -(defmacro consult--slow-operation (message &rest body) - "Show delayed MESSAGE if BODY takes too long. -Also temporarily increase the GC limit via `consult--with-increased-gc'." - (declare (indent 1)) - `(let (set-message-function) ;; bug#63253: Broken `with-delayed-message' - (with-delayed-message (1 ,message) - (consult--with-increased-gc - ,@body)))) - -(defun consult--count-lines (pos) - "Move to position POS and return number of lines." - (let ((line 1)) - (while (< (point) pos) - (forward-line) - (when (<= (point) pos) - (cl-incf line))) - (goto-char pos) - line)) - -(defun consult--marker-from-line-column (buffer line column) - "Get marker in BUFFER from LINE and COLUMN." - (when (buffer-live-p buffer) - (with-current-buffer buffer - (save-excursion - (without-restriction - (goto-char (point-min)) - ;; Location data might be invalid by now! - (ignore-errors - (forward-line (1- line)) - (goto-char (min (+ (point) column) (pos-eol)))) - (point-marker)))))) - -(defun consult--line-prefix (&optional curr-line) - "Annotate `consult-location' candidates with line numbers. -CURR-LINE is the current line number." - (setq curr-line (or curr-line -1)) - (let* ((width (length (number-to-string (line-number-at-pos - (point-max) - consult-line-numbers-widen)))) - (before (format #("%%%dd " 0 6 (face consult-line-number-wrapped)) width)) - (after (format #("%%%dd " 0 6 (face consult-line-number-prefix)) width))) - (lambda (cand) - (let ((line (cdr (get-text-property 0 'consult-location cand)))) - (list cand (format (if (< line curr-line) before after) line) ""))))) - -(defsubst consult--location-candidate (cand marker line tofu &rest props) - "Add MARKER and LINE as `consult-location' text property to CAND. -Furthermore add the additional text properties PROPS, and append -TOFU suffix for disambiguation." - (setq cand (concat cand (consult--tofu-encode tofu))) - (add-text-properties 0 1 `(consult-location (,marker . ,line) ,@props) cand) - cand) - -;; There is a similar variable `yank-excluded-properties'. Unfortunately -;; we cannot use it here since it excludes too much (e.g., invisible) -;; and at the same time not enough (e.g., cursor-sensor-functions). -(defconst consult--remove-text-properties - '(category cursor cursor-intangible cursor-sensor-functions field follow-link - fontified front-sticky help-echo insert-behind-hooks insert-in-front-hooks - intangible keymap local-map modification-hooks mouse-face pointer read-only - rear-nonsticky yank-handler) - "List of text properties to remove from buffer strings.") - -(defsubst consult--buffer-substring (beg end &optional fontify) - "Return buffer substring between BEG and END. -If FONTIFY and `consult-fontify-preserve' are non-nil, first ensure that the -region has been fontified." - (if consult-fontify-preserve - (let (str) - (when fontify (consult--fontify-region beg end)) - (setq str (buffer-substring beg end)) - ;; TODO Propose the upstream addition of a function - ;; `preserve-list-of-text-properties', which should be as efficient as - ;; `remove-list-of-text-properties'. - (remove-list-of-text-properties - 0 (- end beg) consult--remove-text-properties str) - str) - (buffer-substring-no-properties beg end))) - -(defun consult--line-with-mark (marker) - "Current line string where the MARKER position is highlighted." - (let* ((beg (pos-bol)) - (end (pos-eol)) - (str (consult--buffer-substring beg end 'fontify))) - (if (>= marker end) - (concat str #(" " 0 1 (face consult-highlight-mark))) - (put-text-property (- marker beg) (- (1+ marker) beg) - 'face 'consult-highlight-mark str) - str))) - -;;;; Tofu cooks - -(defsubst consult--tofu-p (char) - "Return non-nil if CHAR is a tofu." - (<= consult--tofu-char char (+ consult--tofu-char consult--tofu-range -1))) - -(defun consult--tofu-hide (str) - "Hide the tofus in STR." - (let* ((max (length str)) - (end max)) - (while (and (> end 0) (consult--tofu-p (aref str (1- end)))) - (cl-decf end)) - (when (< end max) - (setq str (copy-sequence str)) - (put-text-property end max 'invisible t str)) - str)) - -(defsubst consult--tofu-append (cand id) - "Append tofu-encoded ID to CAND. -The ID must fit within a single character. It must be smaller -than `consult--tofu-range'." - (setq id (char-to-string (+ consult--tofu-char id))) - (add-text-properties 0 1 '(invisible t consult-strip t) id) - (concat cand id)) - -(defsubst consult--tofu-get (cand) - "Extract tofu-encoded ID from CAND. -See `consult--tofu-append'." - (- (aref cand (1- (length cand))) consult--tofu-char)) - -;; We must disambiguate the lines by adding a prefix such that two lines with -;; the same text can be distinguished. In order to avoid matching the line -;; number, such that the user can search for numbers with `consult-line', we -;; encode the line number as characters outside the Unicode range. By doing -;; that, no accidental matching can occur. -(defun consult--tofu-encode (n) - "Return tofu-encoded number N as a string. -Large numbers are encoded as multiple tofu characters." - (let (str tofu) - (while (progn - (setq tofu (char-to-string - (+ consult--tofu-char (% n consult--tofu-range))) - str (if str (concat tofu str) tofu)) - (and (>= n consult--tofu-range) - (setq n (/ n consult--tofu-range))))) - (add-text-properties 0 (length str) '(invisible t consult-strip t) str) - str)) - -;;;; Regexp utilities - -(defun consult--find-highlights (str start &rest ignored-faces) - "Find highlighted regions in STR from position START. -Highlighted regions have a non-nil face property. -IGNORED-FACES are ignored when searching for matches." - (let (highlights - (end (length str)) - (beg start)) - (while (< beg end) - (let ((next (next-single-property-change beg 'face str end)) - (val (get-text-property beg 'face str))) - (when (and val - (not (memq val ignored-faces)) - (not (and (consp val) - (seq-some (lambda (x) (memq x ignored-faces)) val)))) - (push (cons (- beg start) (- next start)) highlights)) - (setq beg next))) - (nreverse highlights))) - -(defun consult--point-placement (str start &rest ignored-faces) - "Compute point placement from STR with START offset. -IGNORED-FACES are ignored when searching for matches. -Return cons of point position and a list of match begin/end pairs." - (let* ((matches (apply #'consult--find-highlights str start ignored-faces)) - (pos (pcase-exhaustive consult-point-placement - ('match-beginning (or (caar matches) 0)) - ('match-end (or (cdar (last matches)) 0)) - ('line-beginning 0)))) - (dolist (match matches) - (cl-decf (car match) pos) - (cl-decf (cdr match) pos)) - (cons pos matches))) - -(defun consult--highlight-regexps (regexps ignore-case str) - "Highlight REGEXPS in STR. -If a regular expression contains capturing groups, only these are highlighted. -If no capturing groups are used highlight the whole match. Case is ignored -if IGNORE-CASE is non-nil." - (dolist (re regexps) - (let ((i 0)) - (while (and (let ((case-fold-search ignore-case)) - (string-match re str i)) - ;; Ensure that regexp search made progress (edge case for .*) - (> (match-end 0) i)) - ;; Unfortunately there is no way to avoid the allocation of the match - ;; data, since the number of capturing groups is unknown. - (let ((m (match-data))) - (setq i (cadr m) m (or (cddr m) m)) - (while m - (when (car m) - (add-face-text-property (car m) (cadr m) - 'consult-highlight-match nil str)) - (setq m (cddr m))))))) - str) - -(defconst consult--convert-regexp-table - (append - ;; For simplicity, treat word beginning/end as word boundaries, - ;; since PCRE does not make this distinction. Usually the - ;; context determines if \b is the beginning or the end. - '(("\\<" . "\\b") ("\\>" . "\\b") - ("\\_<" . "\\b") ("\\_>" . "\\b")) - ;; Treat \` and \' as beginning and end of line. This is more - ;; widely supported and makes sense for line-based commands. - '(("\\`" . "^") ("\\'" . "$")) - ;; Historical: Unescaped *, +, ? are supported at the beginning - (mapcan (lambda (x) - (mapcar (lambda (y) - (cons (concat x y) - (concat (string-remove-prefix "\\" x) "\\" y))) - '("*" "+" "?"))) - '("" "\\(" "\\(?:" "\\|" "^")) - ;; Different escaping - (mapcan (lambda (x) `(,x (,(cdr x) . ,(car x)))) - '(("\\|" . "|") - ("\\(" . "(") ("\\)" . ")") - ("\\{" . "{") ("\\}" . "}")))) - "Regexp conversion table.") - -(defun consult--convert-regexp (regexp type) - "Convert Emacs REGEXP to regexp syntax TYPE." - (if (memq type '(emacs basic)) - regexp - ;; Support for Emacs regular expressions is fairly complete for basic - ;; usage. There are a few unsupported Emacs regexp features: - ;; - \= point matching - ;; - Syntax classes \sx \Sx - ;; - Character classes \cx \Cx - ;; - Explicitly numbered groups (?3:group) - (replace-regexp-in-string - (rx (or "\\\\" "\\^" ;; Pass through - (seq (or "\\(?:" "\\|") (any "*+?")) ;; Historical: \|+ or \(?:* etc - (seq "\\(" (any "*+")) ;; Historical: \(* or \(+ - (seq (or bos "^") (any "*+?")) ;; Historical: + or * at the beginning - (seq (opt "\\") (any "(){|}")) ;; Escape parens/braces/pipe - (seq "\\" (any "'<>`")) ;; Special escapes - (seq "\\_" (any "<>")))) ;; Beginning or end of symbol - (lambda (x) (or (cdr (assoc x consult--convert-regexp-table)) x)) - regexp 'fixedcase 'literal))) - -(defun consult--default-regexp-compiler (input type ignore-case) - "Compile the INPUT string to a list of regular expressions. -The function should return a pair, the list of regular expressions and a -highlight function. The highlight function should take a single -argument, the string to highlight given the INPUT. TYPE is the desired -type of regular expression, which can be `basic', `extended', `emacs' or -`pcre'. If IGNORE-CASE is non-nil return a highlight function which -matches case insensitively." - (setq input (consult--split-escaped input)) - (cons (mapcar (lambda (x) (consult--convert-regexp x type)) input) - (when-let (regexps (seq-filter #'consult--valid-regexp-p input)) - (apply-partially #'consult--highlight-regexps regexps ignore-case)))) - -(defun consult--split-escaped (str) - "Split STR at spaces, which can be escaped with backslash." - (mapcar - (lambda (x) (string-replace "\0" " " x)) - (split-string (replace-regexp-in-string - "\\\\\\\\\\|\\\\ " - (lambda (x) (if (equal x "\\ ") "\0" x)) - str 'fixedcase 'literal) - " +" t))) - -(defun consult--join-regexps (regexps type) - "Join REGEXPS of TYPE." - ;; Add look-ahead wrapper only if there is more than one regular expression - (cond - ((and (eq type 'pcre) (cdr regexps)) - (concat "^" (mapconcat (lambda (x) (format "(?=.*%s)" x)) - regexps ""))) - ((eq type 'basic) - (string-join regexps ".*")) - (t - (when (length> regexps 3) - (message "Too many regexps, %S ignored. Use post-filtering!" - (string-join (seq-drop regexps 3) " ")) - (setq regexps (seq-take regexps 3))) - (consult--join-regexps-permutations regexps (and (eq type 'emacs) "\\"))))) - -(defun consult--join-regexps-permutations (regexps esc) - "Join all permutations of REGEXPS. -ESC is the escaping string for choice and groups." - (pcase regexps - ('nil "") - (`(,r) r) - (_ (mapconcat - (lambda (r) - (concat esc "(" r esc ").*" esc "(" - (consult--join-regexps-permutations (remove r regexps) esc) - esc ")")) - regexps (concat esc "|"))))) - -(defun consult--valid-regexp-p (re) - "Return t if regexp RE is valid." - (condition-case nil - (progn (string-match-p re "") t) - (invalid-regexp nil))) - -(defun consult--regexp-filter (regexps) - "Create filter regexp from REGEXPS." - (if (stringp regexps) - regexps - (mapconcat (lambda (x) (concat "\\(?:" x "\\)")) regexps "\\|"))) - -;;;; Lookup functions - -(defun consult--lookup-member (selected candidates &rest _) - "Lookup SELECTED in CANDIDATES list, return original element." - (car (member selected candidates))) - -(defun consult--lookup-cons (selected candidates &rest _) - "Lookup SELECTED in CANDIDATES alist, return cons." - (assoc selected candidates)) - -(defun consult--lookup-cdr (selected candidates &rest _) - "Lookup SELECTED in CANDIDATES alist, return `cdr' of element." - (cdr (assoc selected candidates))) - -(defun consult--lookup-location (selected candidates &rest _) - "Lookup SELECTED in CANDIDATES list of `consult-location' category. -Return the location marker." - (when-let (found (member selected candidates)) - (setq found (car (consult--get-location (car found)))) - ;; Check that marker is alive - (and (or (not (markerp found)) (marker-buffer found)) found))) - -(defun consult--lookup-prop (prop selected candidates &rest _) - "Lookup SELECTED in CANDIDATES list and return PROP value." - (when-let (found (member selected candidates)) - (get-text-property 0 prop (car found)))) - -(defun consult--lookup-candidate (selected candidates &rest _) - "Lookup SELECTED in CANDIDATES list and return property `consult--candidate'." - (consult--lookup-prop 'consult--candidate selected candidates)) - -;;;; Preview support - -(defun consult--preview-allowed-p (fun) - "Return non-nil if FUN is an allowed preview mode hook." - (or (memq fun consult-preview-allowed-hooks) - (when-let (((symbolp fun)) - (name (symbol-name fun)) - ;; Global modes in Emacs 29 are activated via a - ;; `find-file-hook' ending with `-check-buffers'. This has been - ;; changed in Emacs 30. Now a `change-major-mode-hook' is used - ;; instead with the suffix `-check-buffers'. - (suffix (static-if (>= emacs-major-version 30) - "-enable-in-buffer" - "-check-buffers")) - ((string-suffix-p suffix name))) - (memq (intern (string-remove-suffix suffix name)) - consult-preview-allowed-hooks)))) - -(defun consult--filter-find-file-hook (orig &rest hooks) - "Filter `find-file-hook' by `consult-preview-allowed-hooks'. -This function is an advice for `run-hooks'. -ORIG is the original function, HOOKS the arguments." - (if (memq 'find-file-hook hooks) - (cl-letf* (((default-value 'find-file-hook) - (seq-filter #'consult--preview-allowed-p - (default-value 'find-file-hook))) - (find-file-hook (default-value 'find-file-hook))) - (apply orig hooks)) - (apply orig hooks))) - -(defun consult--find-file-temporarily-1 (name) - "Open file NAME, helper function for `consult--find-file-temporarily'." - (when-let (((not (seq-find (lambda (x) (string-match-p x name)) - consult-preview-excluded-files))) - ;; file-attributes may throw permission denied error - (attrs (ignore-errors (file-attributes name))) - (size (file-attribute-size attrs))) - (let* ((partial (>= size consult-preview-partial-size)) - (buffer (if partial - (generate-new-buffer (format "consult-partial-preview-%s" name)) - (find-file-noselect name 'nowarn))) - (success nil)) - (unwind-protect - (with-current-buffer buffer - (if (not partial) - (when (or (eq major-mode 'hexl-mode) - (and (eq major-mode 'fundamental-mode) - (save-excursion (search-forward "\0" nil 'noerror)))) - (error "No preview of binary file `%s'" - (file-name-nondirectory name))) - (with-silent-modifications - (setq buffer-read-only t) - (insert-file-contents name nil 0 consult-preview-partial-chunk) - (goto-char (point-max)) - (insert "\nFile truncated. End of partial preview.\n") - (goto-char (point-min))) - (when (save-excursion (search-forward "\0" nil 'noerror)) - (error "No partial preview of binary file `%s'" - (file-name-nondirectory name))) - ;; Auto detect major mode and hope for the best, given that the - ;; file is only previewed partially. If an error is thrown the - ;; buffer will be killed and preview is aborted. - (set-auto-mode) - (font-lock-mode 1)) - (when (bound-and-true-p so-long-detected-p) - (error "No preview of file `%s' with long lines" - (file-name-nondirectory name))) - ;; Run delayed hooks listed in `consult-preview-allowed-hooks'. - (dolist (hook (reverse (cons 'after-change-major-mode-hook delayed-mode-hooks))) - (run-hook-wrapped hook (lambda (fun) - (when (consult--preview-allowed-p fun) - (funcall fun)) - nil))) - (setq success (current-buffer))) - (unless success - (kill-buffer buffer)))))) - -(defun consult--find-file-temporarily (name) - "Open file NAME temporarily for preview." - (let ((vars (delq nil - (mapcar - (pcase-lambda (`(,k . ,v)) - (if (boundp k) - (list k v (default-value k) (symbol-value k)) - (message "consult-preview-variables: The variable `%s' is not bound" k) - nil)) - consult-preview-variables)))) - (condition-case err - (unwind-protect - (progn - (advice-add #'run-hooks :around #'consult--filter-find-file-hook) - (pcase-dolist (`(,k ,v . ,_) vars) - (set-default k v) - (set k v)) - (consult--find-file-temporarily-1 name)) - (advice-remove #'run-hooks #'consult--filter-find-file-hook) - (pcase-dolist (`(,k ,_ ,d ,v) vars) - (set-default k d) - (set k v))) - (error - (message "%s" (error-message-string err)) - nil)))) - -(defun consult--temporary-files () - "Return a function to open files temporarily for preview." - (let ((dir default-directory) - (hook (make-symbol "consult--temporary-files-upgrade-hook")) - (orig-buffers (buffer-list)) - temporary-buffers) - (fset hook - (lambda (_) - ;; Fully initialize previewed files and keep them alive. - (unless (consult--completion-window-p) - (let (live-files) - (pcase-dolist (`(,file . ,buf) temporary-buffers) - (when-let (wins (and (buffer-live-p buf) - (get-buffer-window-list buf))) - (push (cons file (mapcar - (lambda (win) - (cons win (window-state-get win t))) - wins)) - live-files))) - (pcase-dolist (`(,_ . ,buf) temporary-buffers) - (kill-buffer buf)) - (setq temporary-buffers nil) - (pcase-dolist (`(,file . ,wins) live-files) - (when-let (buf (consult--file-action file)) - (push buf orig-buffers) - (pcase-dolist (`(,win . ,state) wins) - (setf (car (alist-get 'buffer state)) buf) - (window-state-put state win)))))))) - (lambda (&optional name) - (if name - (let ((default-directory dir)) - (setq name (abbreviate-file-name (expand-file-name name))) - (or - ;; Find existing fully initialized buffer (non-previewed). We have - ;; to check for fully initialized buffer before accessing the - ;; previewed buffers, since `embark-act' can open a buffer which is - ;; currently previewed, such that we end up with two buffers for - ;; the same file - one previewed and only partially initialized and - ;; one fully initialized. In this case we prefer the fully - ;; initialized buffer. For directories `get-file-buffer' returns nil, - ;; therefore we have to special case Dired. - (if (and (fboundp 'dired-find-buffer-nocreate) (file-directory-p name)) - (dired-find-buffer-nocreate name) - (get-file-buffer name)) - ;; Find existing previewed buffer. Previewed buffers are not fully - ;; initialized (hooks are delayed) in order to ensure fast preview. - (cdr (assoc name temporary-buffers)) - ;; Finally, if no existing buffer has been found, open the file for - ;; preview. - (when-let (buf (consult--find-file-temporarily name)) - ;; Only add new buffer if not already in the list - (unless (or (rassq buf temporary-buffers) (memq buf orig-buffers)) - (add-hook 'window-selection-change-functions hook) - (push (cons name buf) temporary-buffers) - ;; Disassociate buffer from file by setting `buffer-file-name' - ;; and `dired-directory' to nil and rename the buffer. This - ;; lets us open an already previewed buffer with the Embark - ;; default action C-. RET. - (with-current-buffer buf - (rename-buffer - (format " Preview:%s" - (file-name-nondirectory (directory-file-name name))) - 'unique)) - ;; The buffer disassociation is delayed to avoid breaking modes - ;; like `pdf-view-mode' or `doc-view-mode' which rely on - ;; `buffer-file-name'. Executing (set-visited-file-name nil) - ;; early also prevents the major mode initialization. - (let ((hook (make-symbol "consult--temporary-files-disassociate-hook"))) - (fset hook (lambda () - (when (buffer-live-p buf) - (with-current-buffer buf - (remove-hook 'pre-command-hook hook) - (setq-local buffer-read-only t - dired-directory nil - buffer-file-name nil))))) - (add-hook 'pre-command-hook hook)) - ;; Only keep a few buffers alive - (while (length> temporary-buffers consult-preview-max-count) - (kill-buffer (cdar (last temporary-buffers))) - (setq temporary-buffers (nbutlast temporary-buffers)))) - buf))) - (remove-hook 'window-selection-change-functions hook) - (pcase-dolist (`(,_ . ,buf) temporary-buffers) - (kill-buffer buf)) - (setq temporary-buffers nil))))) - -(defun consult--invisible-open-permanently () - "Open overlays which hide the current line. -See `isearch-open-necessary-overlays' and `isearch-open-overlay-temporary'." - (if (and (derived-mode-p 'org-mode) (fboundp 'org-fold-show-set-visibility)) - ;; New Org 9.6 fold-core API - (let ((inhibit-redisplay t)) ;; HACK: Prevent flicker due to premature redisplay - (org-fold-show-set-visibility 'canonical)) - (dolist (ov (overlays-in (pos-bol) (pos-eol))) - (when-let (fun (overlay-get ov 'isearch-open-invisible)) - (when (invisible-p (overlay-get ov 'invisible)) - (funcall fun ov)))))) - -(defun consult--invisible-open-temporarily () - "Temporarily open overlays which hide the current line. -See `isearch-open-necessary-overlays' and `isearch-open-overlay-temporary'." - (if (and (derived-mode-p 'org-mode) - (fboundp 'org-fold-show-set-visibility) - (fboundp 'org-fold-core-get-regions) - (fboundp 'org-fold-core-region)) - ;; New Org 9.6 fold-core API - ;; TODO The provided Org API `org-fold-show-set-visibility' cannot be used - ;; efficiently. We obtain all regions in the whole buffer in order to - ;; restore them. A better show API would return all the applied - ;; modifications such that we can restore the ones which got modified. - (progn - (unless consult--org-fold-regions - (setq consult--org-fold-regions - (delq nil (org-fold-core-get-regions - :with-markers t :from (point-min) :to (point-max)))) - (when consult--org-fold-regions - (let ((hook (make-symbol "consult--invisible-open-temporarily-cleanup-hook")) - (buffer (current-buffer)) - (depth (recursion-depth))) - (fset hook - (lambda () - (when (= (recursion-depth) depth) - (remove-hook 'minibuffer-exit-hook hook) - (run-at-time - 0 nil - (lambda () - (when (buffer-live-p buffer) - (with-current-buffer buffer - (pcase-dolist (`(,beg ,end ,_) consult--org-fold-regions) - (when (markerp beg) (set-marker beg nil)) - (when (markerp end) (set-marker end nil))) - (kill-local-variable 'consult--org-fold-regions)))))))) - (add-hook 'minibuffer-exit-hook hook)))) - (let ((inhibit-redisplay t)) ;; HACK: Prevent flicker due to premature redisplay - (org-fold-show-set-visibility 'canonical)) - (list (lambda () - (pcase-dolist (`(,beg ,end ,spec) consult--org-fold-regions) - (org-fold-core-region beg end t spec))))) - (let (restore) - (dolist (ov (overlays-in (pos-bol) (pos-eol))) - (let ((inv (overlay-get ov 'invisible))) - (when (and (invisible-p inv) (overlay-get ov 'isearch-open-invisible)) - (push (if-let (fun (overlay-get ov 'isearch-open-invisible-temporary)) - (progn - (funcall fun ov nil) - (lambda () (funcall fun ov t))) - (overlay-put ov 'invisible nil) - (lambda () (overlay-put ov 'invisible inv))) - restore)))) - restore))) - -(defun consult--jump-ensure-buffer (pos) - "Ensure that buffer of marker POS is displayed, return t if successful." - (or (not (markerp pos)) - ;; Switch to buffer if it is not visible - (when-let ((buf (marker-buffer pos))) - (or (and (eq (current-buffer) buf) (eq (window-buffer) buf)) - (consult--buffer-action buf 'norecord) - t)))) - -(defun consult--jump (pos) - "Jump to POS. -First push current position to mark ring, then move to new -position and run `consult-after-jump-hook'." - (when pos - ;; Extract marker from list with with overlay positions, see `consult--line-match' - (when (consp pos) (setq pos (car pos))) - ;; When the marker is in the same buffer, record previous location - ;; such that the user can jump back quickly. - (when (or (not (markerp pos)) (eq (current-buffer) (marker-buffer pos))) - ;; push-mark mutates markers in the mark-ring and the mark-marker. - ;; Therefore we transform the marker to a number to be safe. - ;; We all love side effects! - (setq pos (+ pos 0)) - (push-mark (point) t)) - (when (consult--jump-ensure-buffer pos) - (unless (= (goto-char pos) (point)) ;; Widen if jump failed - (widen) - (goto-char pos)) - (consult--invisible-open-permanently) - (run-hooks 'consult-after-jump-hook))) - nil) - -(defun consult--jump-preview () - "The preview function used if selecting from a list of candidate positions. -The function can be used as the `:state' argument of `consult--read'." - (let (restore) - (lambda (action cand) - (when (eq action 'preview) - (mapc #'funcall restore) - (setq restore nil) - ;; TODO Better buffer preview support - ;; 1. Use consult--buffer-preview instead of consult--jump-ensure-buffer - ;; 2. Remove function consult--jump-ensure-buffer - ;; 3. Remove consult-buffer-other-* from consult-customize-alist - (when-let ((pos (or (car-safe cand) cand)) ;; Candidate can be previewed - ((consult--jump-ensure-buffer pos))) - (let ((saved-min (point-min-marker)) - (saved-max (point-max-marker)) - (saved-pos (point-marker))) - (set-marker-insertion-type saved-max t) ;; Grow when text is inserted - (push (lambda () - (when-let ((buf (marker-buffer saved-pos))) - (with-current-buffer buf - (narrow-to-region saved-min saved-max) - (goto-char saved-pos) - (set-marker saved-pos nil) - (set-marker saved-min nil) - (set-marker saved-max nil)))) - restore)) - (unless (= (goto-char pos) (point)) ;; Widen if jump failed - (widen) - (goto-char pos)) - (setq restore (nconc (consult--invisible-open-temporarily) restore)) - ;; Ensure that cursor is properly previewed (gh:minad/consult#764) - (unless (eq cursor-in-non-selected-windows 'box) - (let ((orig cursor-in-non-selected-windows) - (buf (current-buffer))) - (push - (if (local-variable-p 'cursor-in-non-selected-windows) - (lambda () - (when (buffer-live-p buf) - (with-current-buffer buf - (setq-local cursor-in-non-selected-windows orig)))) - (lambda () - (when (buffer-live-p buf) - (with-current-buffer buf - (kill-local-variable 'cursor-in-non-selected-windows))))) - restore) - (setq-local cursor-in-non-selected-windows 'box))) - ;; Match previews - (let ((overlays - (list (save-excursion - (let ((vbeg (progn (beginning-of-visual-line) (point))) - (vend (progn (end-of-visual-line) (point))) - (end (pos-eol))) - (consult--make-overlay vbeg (if (= vend end) (1+ end) vend) - 'face 'consult-preview-line - 'window (selected-window) - 'priority 1)))))) - (dolist (match (cdr-safe cand)) - (push (consult--make-overlay (+ (point) (car match)) - (+ (point) (cdr match)) - 'face 'consult-preview-match - 'window (selected-window) - 'priority 2) - overlays)) - (push (lambda () (mapc #'delete-overlay overlays)) restore)) - (run-hooks 'consult-after-jump-hook)))))) - -(defun consult--jump-state () - "The state function used if selecting from a list of candidate positions." - (consult--state-with-return (consult--jump-preview) #'consult--jump)) - -(defun consult--get-location (cand) - "Return location from CAND." - (let ((loc (get-text-property 0 'consult-location cand))) - (when (consp (car loc)) - ;; Transform cheap marker to real marker - (setcar loc (set-marker (make-marker) (cdar loc) (caar loc)))) - loc)) - -(defun consult--location-state (candidates) - "Location state function. -The cheap location markers from CANDIDATES are upgraded on window -selection change to full Emacs markers." - (let ((jump (consult--jump-state)) - (hook (make-symbol "consult--location-upgrade-hook"))) - (fset hook - (lambda (_) - (unless (consult--completion-window-p) - (remove-hook 'window-selection-change-functions hook) - (mapc #'consult--get-location - (if (functionp candidates) (funcall candidates) candidates))))) - (lambda (action cand) - (pcase action - ('setup (add-hook 'window-selection-change-functions hook)) - ('exit (remove-hook 'window-selection-change-functions hook))) - (funcall jump action cand)))) - -(defun consult--state-with-return (state return) - "Compose STATE function with RETURN function." - (lambda (action cand) - (funcall state action cand) - (when (and cand (eq action 'return)) - (funcall return cand)))) - -(defmacro consult--define-state (type) - "Define state function for TYPE." - `(defun ,(intern (format "consult--%s-state" type)) () - ,(format "State function for %ss with preview. -The result can be passed as :state argument to `consult--read'." type) - (consult--state-with-return (,(intern (format "consult--%s-preview" type))) - #',(intern (format "consult--%s-action" type))))) - -(defun consult--preview-key-normalize (preview-key) - "Normalize PREVIEW-KEY, return alist of keys and debounce times." - (let ((keys) - (debounce 0)) - (setq preview-key (ensure-list preview-key)) - (while preview-key - (if (eq (car preview-key) :debounce) - (setq debounce (cadr preview-key) - preview-key (cddr preview-key)) - (let ((key (car preview-key))) - (unless (eq key 'any) - (setq key (consult--key-parse key))) - (push (cons key debounce) keys)) - (pop preview-key))) - keys)) - -(defun consult--preview-key-debounce (preview-key cand) - "Return debounce value of PREVIEW-KEY given the current candidate CAND." - (when (and (consp preview-key) (memq :keys preview-key)) - (setq preview-key (funcall (plist-get preview-key :predicate) cand))) - (let ((map (make-sparse-keymap)) - (keys (this-single-command-keys)) - any) - (pcase-dolist (`(,k . ,d) (consult--preview-key-normalize preview-key)) - (if (eq k 'any) - (setq any d) - (define-key map k `(lambda () ,d)))) - (setq keys (lookup-key map keys)) - (if (functionp keys) (funcall keys) any))) - -(defun consult--preview-append-local-pch (fun) - "Append FUN to local `post-command-hook' list." - ;; Symbol indirection because of bug#46407. - (let ((hook (make-symbol "consult--preview-post-command-hook"))) - (fset hook fun) - ;; TODO Emacs 28 has a bug, where the hook--depth-alist is not cleaned up properly - ;; Do not use the broken add-hook here. - ;;(add-hook 'post-command-hook hook 'append 'local) - (setq-local post-command-hook - (append - (remove t post-command-hook) - (list hook) - (and (memq t post-command-hook) '(t)))))) - -(defun consult--with-preview-1 (preview-key state transform candidate save-input fun) - "Add preview support for FUN. -See `consult--with-preview' for the arguments -PREVIEW-KEY, STATE, TRANSFORM, CANDIDATE and SAVE-INPUT." - (let ((mb-input "") mb-narrow selected timer previewed) - (consult--minibuffer-with-setup-hook - (if (and state preview-key) - (lambda () - (let ((hook (make-symbol "consult--preview-minibuffer-exit-hook")) - (depth (recursion-depth))) - (fset hook - (lambda () - (when (= (recursion-depth) depth) - (remove-hook 'minibuffer-exit-hook hook) - (when timer - (cancel-timer timer) - (setq timer nil)) - (with-selected-window (consult--original-window) - ;; STEP 3: Reset preview - (when previewed - (funcall state 'preview nil)) - ;; STEP 4: Notify the preview function of the minibuffer exit - (funcall state 'exit nil))))) - (add-hook 'minibuffer-exit-hook hook)) - ;; STEP 1: Setup the preview function - (with-selected-window (consult--original-window) - (funcall state 'setup nil)) - (setq consult--preview-function - (lambda () - (when-let ((cand (funcall candidate))) - ;; Drop properties to prevent bugs regarding candidate - ;; lookup, which must handle candidates without - ;; properties. Otherwise the arguments passed to the - ;; lookup function are confusing, since during preview - ;; the candidate has properties but for the final lookup - ;; after completion it does not. - (setq cand (substring-no-properties cand)) - (with-selected-window (active-minibuffer-window) - (let ((input (minibuffer-contents-no-properties)) - (narrow consult--narrow) - (win (consult--original-window))) - (with-selected-window win - (when-let ((transformed (funcall transform narrow input cand)) - (debounce (consult--preview-key-debounce preview-key transformed))) - (when timer - (cancel-timer timer) - (setq timer nil)) - ;; The transformed candidate may have text - ;; properties, which change the preview display. - ;; This matters for example for `consult-grep', - ;; where the current candidate and input may - ;; stay equal, but the highlighting of the - ;; candidate changes while the candidates list - ;; is lagging a bit behind and updates - ;; asynchronously. - ;; - ;; In older Consult versions we instead compared - ;; the input without properties, since I worried - ;; that comparing the transformed candidates - ;; could be potentially expensive. However - ;; comparing the transformed candidates is more - ;; correct. The transformed candidate is the - ;; thing which is actually previewed. - (unless (equal-including-properties previewed transformed) - (if (> debounce 0) - (setq timer - (run-at-time - debounce nil - (lambda () - ;; Preview only when a completion - ;; window is selected and when - ;; the preview window is alive. - (when (and (consult--completion-window-p) - (window-live-p win)) - (with-selected-window win - ;; STEP 2: Preview candidate - (funcall state 'preview (setq previewed transformed))))))) - ;; STEP 2: Preview candidate - (funcall state 'preview (setq previewed transformed))))))))))) - (consult--preview-append-local-pch - (lambda () - (setq mb-input (minibuffer-contents-no-properties) - mb-narrow consult--narrow) - (funcall consult--preview-function)))) - (lambda () - (consult--preview-append-local-pch - (lambda () - (setq mb-input (minibuffer-contents-no-properties) - mb-narrow consult--narrow))))) - (unwind-protect - (setq selected (when-let (result (funcall fun)) - (when-let ((save-input) - (list (symbol-value save-input)) - ((equal (car list) result))) - (set save-input (cdr list))) - (funcall transform mb-narrow mb-input result))) - (when save-input - (add-to-history save-input mb-input)) - (when state - ;; STEP 5: The preview function should perform its final action - (funcall state 'return selected)))))) - -(defmacro consult--with-preview (preview-key state transform candidate save-input &rest body) - "Add preview support to BODY. - -STATE is the state function. -TRANSFORM is the transformation function. -CANDIDATE is the function returning the current candidate. -PREVIEW-KEY are the keys which triggers the preview. -SAVE-INPUT can be a history variable symbol to save the input. - -The state function takes two arguments, an action argument and the -selected candidate. The candidate argument can be nil if no candidate is -selected or if the selection was aborted. The function is called in -sequence with the following arguments: - - 1. \\='setup nil After entering the mb (minibuffer-setup-hook). -⎧ 2. \\='preview CAND/nil Preview candidate CAND or reset if CAND is nil. -⎪ \\='preview CAND/nil -⎪ \\='preview CAND/nil -⎪ ... -⎩ 3. \\='preview nil Reset preview. - 4. \\='exit nil Before exiting the mb (minibuffer-exit-hook). - 5. \\='return CAND/nil After leaving the mb, CAND has been selected. - -The state function is always executed with the original window selected, -see `consult--original-window'. The state function is called once in -the beginning of the minibuffer setup with the `setup' argument. This is -useful in order to perform certain setup operations which require that -the minibuffer is initialized. During completion candidates are -previewed. Then the function is called with the `preview' argument and a -candidate CAND or nil if no candidate is selected. Furthermore if nil is -passed for CAND, then the preview must be undone and the original state -must be restored. The call with the `exit' argument happens once at the -end of the completion process, just before exiting the minibuffer. The -minibuffer is still alive at that point. Both `setup' and `exit' are -only useful for setup and cleanup operations. They don't receive a -candidate as argument. After leaving the minibuffer, the selected -candidate or nil is passed to the state function with the action -argument `return'. At this point the state function can perform the -actual action on the candidate. The state function with the `return' -argument is the continuation of `consult--read'. Via `unwind-protect' it -is guaranteed, that if the `setup' action of a state function is -invoked, the state function will also be called with `exit' and -`return'." - (declare (indent 5)) - `(consult--with-preview-1 ,preview-key ,state ,transform ,candidate ,save-input (lambda () ,@body))) - -;;;; Narrowing and grouping - -(defun consult--prefix-group (cand transform) - "Return title for CAND or TRANSFORM the candidate. -The candidate must have a `consult--prefix-group' property." - (if transform - (substring cand (1+ (length (get-text-property 0 'consult--prefix-group cand)))) - (get-text-property 0 'consult--prefix-group cand))) - -(defun consult--type-group (types) - "Return group function for TYPES." - (lambda (cand transform) - (if transform cand - (alist-get (get-text-property 0 'consult--type cand) types)))) - -(defun consult--type-narrow (types) - "Return narrowing configuration from TYPES." - (list :predicate - (lambda (cand) (eq (get-text-property 0 'consult--type cand) consult--narrow)) - :keys types)) - -(defun consult--widen-key () - "Return widening key, if `consult-widen-key' is not set. -The default is twice the `consult-narrow-key'." - (cond - (consult-widen-key - (consult--key-parse consult-widen-key)) - (consult-narrow-key - (let ((key (consult--key-parse consult-narrow-key))) - (vconcat key key))))) - -(defun consult-narrow (key) - "Narrow current completion with KEY. - -This command is used internally by the narrowing system of `consult--read'." - (interactive - (list (unless (equal (this-single-command-keys) (consult--widen-key)) - last-command-event))) - (consult--require-minibuffer) - (setq consult--narrow key) - (when consult--narrow-predicate - (setq minibuffer-completion-predicate (and consult--narrow consult--narrow-predicate))) - (when consult--narrow-overlay - (delete-overlay consult--narrow-overlay)) - (when consult--narrow - (setq consult--narrow-overlay - (consult--make-overlay - (1- (minibuffer-prompt-end)) (minibuffer-prompt-end) - 'before-string - (propertize (format " [%s]" (alist-get consult--narrow - consult--narrow-keys)) - 'face 'consult-narrow-indicator)))) - (run-hooks 'consult--completion-refresh-hook)) - -(defconst consult--narrow-delete - `(menu-item - "" nil :filter - ,(lambda (&optional _) - (when (equal (minibuffer-contents-no-properties) "") - (lambda () - (interactive) - (consult-narrow nil)))))) - -(defconst consult--narrow-space - `(menu-item - "" nil :filter - ,(lambda (&optional _) - (let ((str (minibuffer-contents-no-properties))) - (when-let (pair (or (and (length= str 1) - (assoc (aref str 0) consult--narrow-keys)) - (and (equal str "") - (assoc ?\s consult--narrow-keys)))) - (lambda () - (interactive) - (delete-minibuffer-contents) - (consult-narrow (car pair)))))))) - -(defun consult-narrow-help () - "Print narrowing help as a `minibuffer-message'. - -This command can be bound to a key in `consult-narrow-map', -to make it available for commands with narrowing." - (interactive) - (consult--require-minibuffer) - (let ((minibuffer-message-timeout 1000000)) - (minibuffer-message - (mapconcat (lambda (x) - (concat - (propertize (key-description (list (car x))) 'face 'consult-key) - " " - (propertize (cdr x) 'face 'consult-help))) - consult--narrow-keys - " ")))) - -(defun consult--narrow-setup (settings map) - "Setup narrowing with SETTINGS and keymap MAP." - (if (memq :keys settings) - (setq consult--narrow-predicate (plist-get settings :predicate) - consult--narrow-keys (plist-get settings :keys)) - (setq consult--narrow-predicate nil - consult--narrow-keys settings)) - (when-let ((key consult-narrow-key)) - (setq key (consult--key-parse key)) - (dolist (pair consult--narrow-keys) - (define-key map (vconcat key (vector (car pair))) - (cons (cdr pair) #'consult-narrow)))) - (when-let ((widen (consult--widen-key))) - (define-key map widen (cons "All" #'consult-narrow))) - (when-let ((init (and (memq :keys settings) (plist-get settings :initial)))) - (consult-narrow init))) - -;; Emacs 28: hide in M-X -(put #'consult-narrow-help 'completion-predicate #'ignore) -(put #'consult-narrow 'completion-predicate #'ignore) - -;;;; Splitting completion style - -(defun consult--split-perl (str &optional _plist) - "Split input STR in async input and filtering part. - -The function returns a list with three elements: The async -string, the start position of the completion filter string and a -force flag. If the first character is a punctuation character it -determines the separator. Examples: \"/async/filter\", -\"#async#filter\"." - (if (string-match-p "^[[:punct:]]" str) - (save-match-data - (let ((q (regexp-quote (substring str 0 1)))) - (string-match (concat "^" q "\\([^" q "]*\\)\\(" q "\\)?") str) - `(,(match-string 1 str) - ,(match-end 0) - ;; Force update it two punctuation characters are entered. - ,(match-end 2) - ;; List of highlights - (0 . ,(match-beginning 1)) - ,@(and (match-end 2) `((,(match-beginning 2) . ,(match-end 2))))))) - `(,str ,(length str)))) - -(defun consult--split-nil (str &optional _plist) - "Treat the complete input STR as async input." - `(,str ,(length str))) - -(defun consult--split-separator (str plist) - "Split input STR in async input and filtering part at first separator. -PLIST is the splitter configuration, including the separator." - (let ((sep (regexp-quote (char-to-string (plist-get plist :separator))))) - (save-match-data - (if (string-match (format "^\\([^%s]+\\)\\(%s\\)?" sep sep) str) - `(,(match-string 1 str) - ,(match-end 0) - ;; Force update it space is entered. - ,(match-end 2) - ;; List of highlights - ,@(and (match-end 2) `((,(match-beginning 2) . ,(match-end 2))))) - `(,str ,(length str)))))) - -(defun consult--split-setup (split) - "Setup splitting completion style with splitter function SPLIT." - (let* ((styles completion-styles) - (catdef completion-category-defaults) - (catovr completion-category-overrides) - (try (lambda (str table pred point) - (let ((completion-styles styles) - (completion-category-defaults catdef) - (completion-category-overrides catovr) - (pos (cadr (funcall split str)))) - (pcase (completion-try-completion (substring str pos) table pred - (max 0 (- point pos))) - ('t t) - (`(,newstr . ,newpt) - (cons (concat (substring str 0 pos) newstr) - (+ pos newpt))))))) - (all (lambda (str table pred point) - (let ((completion-styles styles) - (completion-category-defaults catdef) - (completion-category-overrides catovr) - (pos (cadr (funcall split str)))) - (completion-all-completions (substring str pos) table pred - (max 0 (- point pos))))))) - (setq-local completion-styles-alist (cons `(consult--split ,try ,all "") - completion-styles-alist) - completion-styles '(consult--split) - completion-category-defaults nil - completion-category-overrides nil))) - -;;;; Asynchronous filtering functions - -(defun consult--async-p (fun) - "Return t if FUN is an asynchronous completion function." - (and (functionp fun) - (condition-case nil - (progn (funcall fun "" nil 'metadata) nil) - (wrong-number-of-arguments t)))) - -(defmacro consult--with-async (bind &rest body) - "Setup asynchronous completion in BODY. - -BIND is the asynchronous function binding." - (declare (indent 1)) - (let ((async (car bind))) - `(let ((,async ,@(cdr bind)) - (new-chunk (max read-process-output-max consult--process-chunk)) - orig-chunk) - (consult--minibuffer-with-setup-hook - ;; Append such that we overwrite the completion style setting of - ;; `fido-mode'. See `consult--async-split' and - ;; `consult--split-setup'. - (:append - (lambda () - (when (consult--async-p ,async) - (setq orig-chunk read-process-output-max - read-process-output-max new-chunk) - (funcall ,async 'setup) - (let* ((mb (current-buffer)) - (fun (lambda () - (when-let (win (active-minibuffer-window)) - (when (eq (window-buffer win) mb) - (with-current-buffer mb - (let ((inhibit-modification-hooks t)) - ;; Push input string to request refresh. - (funcall ,async (minibuffer-contents-no-properties)))))))) - ;; We use a symbol in order to avoid adding lambdas to - ;; the hook variable. Symbol indirection because of - ;; bug#46407. - (hook (make-symbol "consult--async-after-change-hook"))) - ;; Delay modification hook to ensure that minibuffer is still - ;; alive after the change, such that we don't restart a new - ;; asynchronous search right before exiting the minibuffer. - (fset hook (lambda (&rest _) (run-at-time 0 nil fun))) - (add-hook 'after-change-functions hook nil 'local) - (funcall hook))))) - (let ((,async (if (consult--async-p ,async) ,async (lambda (_) ,async)))) - (unwind-protect - ,(macroexp-progn body) - (funcall ,async 'destroy) - (when (and orig-chunk (eq read-process-output-max new-chunk)) - (setq read-process-output-max orig-chunk)))))))) - -(defun consult--async-sink () - "Create ASYNC sink function. - -An async function must accept a single action argument. For the -\\='setup action it is guaranteed that the call originates from -the minibuffer. For the other actions no assumption about the -context can be made. - -\\='setup Setup the internal closure state. Return nil. -\\='destroy Destroy the internal closure state. Return nil. -\\='flush Flush the list of candidates. Return nil. -\\='refresh Request UI refresh. Return nil. -nil Return the list of candidates. -list Append the list to the already existing candidates list and return it. -string Update with the current user input string. Return nil." - (let (candidates last buffer) - (lambda (action) - (pcase-exhaustive action - ('setup - (setq buffer (current-buffer)) - nil) - ((or (pred stringp) 'destroy) nil) - ('flush (setq candidates nil last nil)) - ('refresh - ;; Refresh the UI when the current minibuffer window belongs - ;; to the current asynchronous completion session. - (when-let (win (active-minibuffer-window)) - (when (eq (window-buffer win) buffer) - (with-selected-window win - (run-hooks 'consult--completion-refresh-hook) - ;; Interaction between asynchronous completion functions and - ;; preview: We have to trigger preview immediately when - ;; candidates arrive (gh:minad/consult#436). - (when (and consult--preview-function candidates) - (funcall consult--preview-function))))) - nil) - ('nil candidates) - ((pred consp) - (setq last (last (if last (setcdr last action) (setq candidates action)))) - candidates))))) - -(defun consult--async-split-style () - "Return the async splitting style function and initial string." - (or (alist-get consult-async-split-style consult-async-split-styles-alist) - (user-error "Splitting style `%s' not found" consult-async-split-style))) - -(defun consult--async-split-initial (initial) - "Return initial string for async command. -INITIAL is the additional initial string." - (concat (plist-get (consult--async-split-style) :initial) initial)) - -(defun consult--async-split-thingatpt (thing) - "Return THING at point with async initial prefix." - (when-let (str (thing-at-point thing)) - (consult--async-split-initial str))) - -(defun consult--async-split (async &optional split) - "Create async function, which splits the input string. -ASYNC is the async sink. -SPLIT is the splitting function." - (unless split - (let* ((style (consult--async-split-style)) - (fn (plist-get style :function))) - (setq split (lambda (str) (funcall fn str style))))) - (lambda (action) - (pcase action - ('setup - (consult--split-setup split) - (funcall async 'setup)) - ((pred stringp) - (pcase-let* ((`(,async-str ,_ ,force . ,highlights) - (funcall split action)) - (async-len (length async-str)) - (input-len (length action)) - (end (minibuffer-prompt-end))) - ;; Highlight punctuation characters - (remove-list-of-text-properties end (+ end input-len) '(face)) - (dolist (hl highlights) - (put-text-property (+ end (car hl)) (+ end (cdr hl)) - 'face 'consult-async-split)) - (funcall async - ;; Pass through if the input is long enough! - (if (or force (>= async-len consult-async-min-input)) - async-str - ;; Pretend that there is no input - "")))) - (_ (funcall async action))))) - -(defun consult--async-indicator (async) - "Create async function with a state indicator overlay. -ASYNC is the async sink." - (let (ov) - (lambda (action &optional state) - (pcase action - ('indicator - (overlay-put ov 'display - (pcase-exhaustive state - ('running #("*" 0 1 (face consult-async-running))) - ('finished #(":" 0 1 (face consult-async-finished))) - ('killed #(";" 0 1 (face consult-async-failed))) - ('failed #("!" 0 1 (face consult-async-failed)))))) - ('setup - (setq ov (make-overlay (- (minibuffer-prompt-end) 2) - (- (minibuffer-prompt-end) 1))) - (funcall async 'setup)) - ('destroy - (delete-overlay ov) - (funcall async 'destroy)) - (_ (funcall async action)))))) - -(defun consult--async-log (formatted &rest args) - "Log FORMATTED ARGS to variable `consult--async-log'." - (with-current-buffer (get-buffer-create consult--async-log) - (goto-char (point-max)) - (insert (apply #'format formatted args)))) - -(defun consult--async-process (async builder &rest props) - "Create process source async function. - -ASYNC is the async function which receives the candidates. -BUILDER is the command line builder function. -PROPS are optional properties passed to `make-process'." - (setq async (consult--async-indicator async)) - (let (proc proc-buf last-args count) - (lambda (action) - (pcase action - ("" ;; If no input is provided kill current process - (when proc - (delete-process proc) - (kill-buffer proc-buf) - (setq proc nil proc-buf nil)) - (setq last-args nil)) - ((pred stringp) - (funcall async action) - (let* ((args (funcall builder action))) - (unless (stringp (car args)) - (setq args (car args))) - (unless (equal args last-args) - (setq last-args args) - (when proc - (delete-process proc) - (kill-buffer proc-buf) - (setq proc nil proc-buf nil)) - (when args - (let* ((flush t) - (rest "") - (proc-filter - (lambda (_ out) - (when flush - (setq flush nil) - (funcall async 'flush)) - (let ((lines (split-string out "[\r\n]+"))) - (if (not (cdr lines)) - (setq rest (concat rest (car lines))) - (setcar lines (concat rest (car lines))) - (let* ((len (length lines)) - (last (nthcdr (- len 2) lines))) - (setq rest (cadr last) - count (+ count len -1)) - (setcdr last nil) - (funcall async lines)))))) - (proc-sentinel - (lambda (_ event) - (when flush - (setq flush nil) - (funcall async 'flush)) - (funcall async 'indicator - (cond - ((string-prefix-p "killed" event) 'killed) - ((string-prefix-p "finished" event) 'finished) - (t 'failed))) - (when (and (string-prefix-p "finished" event) (not (equal rest ""))) - (cl-incf count) - (funcall async (list rest))) - (consult--async-log - "consult--async-process sentinel: event=%s lines=%d\n" - (string-trim event) count) - (when (> (buffer-size proc-buf) 0) - (with-current-buffer (get-buffer-create consult--async-log) - (goto-char (point-max)) - (insert ">>>>> stderr >>>>>\n") - (let ((beg (point))) - (insert-buffer-substring proc-buf) - (save-excursion - (goto-char beg) - (message #("%s" 0 2 (face error)) - (buffer-substring-no-properties (pos-bol) (pos-eol))))) - (insert "<<<<< stderr <<<<<\n"))))) - (process-adaptive-read-buffering nil)) - (funcall async 'indicator 'running) - (consult--async-log "consult--async-process started %S\n" args) - (setq count 0 - proc-buf (generate-new-buffer " *consult-async-stderr*") - proc (apply #'make-process - `(,@props - :connection-type pipe - :name ,(car args) - ;;; XXX tramp bug, the stderr buffer must be empty - :stderr ,proc-buf - :noquery t - :command ,args - :filter ,proc-filter - :sentinel ,proc-sentinel))))))) - nil) - ('destroy - (when proc - (delete-process proc) - (kill-buffer proc-buf) - (setq proc nil proc-buf nil)) - (funcall async 'destroy)) - (_ (funcall async action)))))) - -(defun consult--async-highlight (async builder) - "Return a new ASYNC function with candidate highlighting. -BUILDER is the command line builder function." - (let (highlight) - (lambda (action) - (cond - ((stringp action) - (setq highlight (cdr (funcall builder action))) - (funcall async action)) - ((and (consp action) highlight) - (dolist (str action) - (funcall highlight str)) - (funcall async action)) - (t (funcall async action)))))) - -(defun consult--async-throttle (async &optional throttle debounce) - "Create async function from ASYNC which throttles input. - -The THROTTLE delay defaults to `consult-async-input-throttle'. -The DEBOUNCE delay defaults to `consult-async-input-debounce'." - (setq throttle (or throttle consult-async-input-throttle) - debounce (or debounce consult-async-input-debounce)) - (let* ((input "") (timer (timer-create)) (last 0)) - (lambda (action) - (pcase action - ((pred stringp) - (unless (equal action input) - (cancel-timer timer) - (funcall async "") ;; cancel running process - (setq input action) - (unless (equal action "") - (timer-set-function timer (lambda () - (setq last (float-time)) - (funcall async action))) - (timer-set-time - timer - (timer-relative-time - nil (max debounce (- (+ last throttle) (float-time))))) - (timer-activate timer))) - nil) - ('destroy - (cancel-timer timer) - (funcall async 'destroy)) - (_ (funcall async action)))))) - -(defun consult--async-refresh-immediate (async) - "Create async function from ASYNC, which refreshes the display. - -The refresh happens immediately when candidates are pushed." - (lambda (action) - (pcase action - ((or (pred consp) 'flush) - (prog1 (funcall async action) - (funcall async 'refresh))) - (_ (funcall async action))))) - -(defun consult--async-refresh-timer (async &optional delay) - "Create async function from ASYNC, which refreshes the display. - -The refresh happens after a DELAY, defaulting to `consult-async-refresh-delay'." - (let ((delay (or delay consult-async-refresh-delay)) - (timer (timer-create))) - (timer-set-function timer async '(refresh)) - (lambda (action) - (prog1 (funcall async action) - (pcase action - ((or (pred consp) 'flush) - (unless (memq timer timer-list) - (timer-set-time timer (timer-relative-time nil delay)) - (timer-activate timer))) - ('destroy - (cancel-timer timer))))))) - -(defmacro consult--async-command (builder &rest args) - "Asynchronous command pipeline. -ARGS is a list of `make-process' properties and transforms. -BUILDER is the command line builder function, which takes the -input string and must either return a list of command line -arguments or a pair of the command line argument list and a -highlighting function." - (declare (indent 1)) - `(thread-first - (consult--async-sink) - (consult--async-refresh-timer) - ,@(seq-take-while (lambda (x) (not (keywordp x))) args) - (consult--async-process - ,builder - ,@(seq-drop-while (lambda (x) (not (keywordp x))) args)) - (consult--async-throttle) - (consult--async-split))) - -(defmacro consult--async-transform (async &rest transform) - "Use FUN to TRANSFORM candidates of ASYNC." - (cl-with-gensyms (async-var action-var) - `(let ((,async-var ,async)) - (lambda (,action-var) - (funcall ,async-var (if (consp ,action-var) (,@transform ,action-var) ,action-var)))))) - -(defun consult--async-map (async fun) - "Map candidates of ASYNC by FUN." - (consult--async-transform async mapcar fun)) - -(defun consult--async-filter (async fun) - "Filter candidates of ASYNC by FUN." - (consult--async-transform async seq-filter fun)) - -;;;; Dynamic collections based - -(defun consult--dynamic-compute (async fun &optional debounce) - "Dynamic computation of candidates. -ASYNC is the sink. -FUN computes the candidates given the input. -DEBOUNCE is the time after which an interrupted computation -should be restarted." - (setq debounce (or debounce consult-async-input-debounce)) - (setq async (consult--async-indicator async)) - (let* ((request) (current) (timer) - (cancel (lambda () (when timer (cancel-timer timer) (setq timer nil)))) - (start (lambda (req) (setq request req) (funcall async 'refresh)))) - (lambda (action) - (pcase action - ((and 'nil (guard (not request))) - (funcall async nil)) - ('nil - (funcall cancel) - (let ((state 'killed)) - (unwind-protect - (progn - (funcall async 'indicator 'running) - (redisplay) - ;; Run computation - (let ((response (funcall fun request))) - ;; Flush and update candidate list - (funcall async 'flush) - (setq state 'finished current request) - (funcall async response))) - (funcall async 'indicator state) - ;; If the computation was killed, restart it after some time. - (when (eq state 'killed) - (setq timer (run-at-time debounce nil start request))) - (setq request nil)))) - ((pred stringp) - (funcall cancel) - (if (or (equal action "") (equal action current)) - (funcall async 'indicator 'finished) - (funcall start action))) - ('destroy - (funcall cancel) - (funcall async 'destroy)) - (_ (funcall async action)))))) - -(defun consult--dynamic-collection (fun) - "Dynamic collection with input splitting. -FUN computes the candidates given the input." - (thread-first - (consult--async-sink) - (consult--dynamic-compute fun) - (consult--async-throttle) - (consult--async-split))) - -;;;; Special keymaps - -(defvar-keymap consult-async-map - :doc "Keymap added for commands with asynchronous candidates." - ;; Overwriting some unusable defaults of default minibuffer completion. - "<remap> <minibuffer-complete-word>" #'self-insert-command - ;; Remap Emacs 29 history and default completion for now - ;; (gh:minad/consult#613). - "<remap> <minibuffer-complete-defaults>" #'ignore - "<remap> <minibuffer-complete-history>" #'consult-history) - -(defvar-keymap consult-narrow-map - :doc "Narrowing keymap which is added to the local minibuffer map. -Note that `consult-narrow-key' and `consult-widen-key' are bound dynamically." - "SPC" consult--narrow-space - "DEL" consult--narrow-delete) - -;;;; Internal API: consult--read - -(defun consult--annotate-align (cand ann) - "Align annotation ANN by computing the maximum CAND width." - (setq consult--annotate-align-width - (max consult--annotate-align-width - (* (ceiling (consult--display-width cand) - consult--annotate-align-step) - consult--annotate-align-step))) - (when ann - (concat - #(" " 0 1 (display (space :align-to (+ left consult--annotate-align-width)))) - ann))) - -(defun consult--add-history (async items) - "Add ITEMS to the minibuffer future history. -ASYNC must be non-nil for async completion functions." - (delete-dups - (append - ;; the defaults are at the beginning of the future history - (ensure-list minibuffer-default) - ;; then our custom items - (remove "" (remq nil (ensure-list items))) - ;; Add all the completions for non-async commands. For async commands this - ;; feature is not useful, since if one selects a completion candidate, the - ;; async search is restarted using that candidate string. This usually does - ;; not yield a desired result since the async input uses a special format, - ;; e.g., `#grep#filter'. - (unless async - (all-completions "" - minibuffer-completion-table - minibuffer-completion-predicate))))) - -(defun consult--setup-keymap (keymap async narrow preview-key) - "Setup minibuffer keymap. - -KEYMAP is a command-specific keymap. -ASYNC must be non-nil for async completion functions. -NARROW are the narrow settings. -PREVIEW-KEY are the preview keys." - (let ((old-map (current-local-map)) - (map (make-sparse-keymap))) - - ;; Add narrow keys - (when narrow - (consult--narrow-setup narrow map)) - - ;; Preview trigger keys - (when (and (consp preview-key) (memq :keys preview-key)) - (setq preview-key (plist-get preview-key :keys))) - (setq preview-key (mapcar #'car (consult--preview-key-normalize preview-key))) - (when preview-key - (dolist (key preview-key) - (unless (or (eq key 'any) (lookup-key old-map key)) - (define-key map key #'ignore)))) - - ;; Put the keymap together - (use-local-map - (make-composed-keymap - (delq nil (list keymap - (and async consult-async-map) - (and narrow consult-narrow-map) - map)) - old-map)))) - -(defun consult--tofu-hide-in-minibuffer (&rest _) - "Hide the tofus in the minibuffer." - (let* ((min (minibuffer-prompt-end)) - (max (point-max)) - (pos max)) - (while (and (> pos min) (consult--tofu-p (char-before pos))) - (cl-decf pos)) - (when (< pos max) - (add-text-properties pos max '(invisible t rear-nonsticky t cursor-intangible t))))) - -(defun consult--read-annotate (fun cand) - "Annotate CAND with annotation function FUN." - (pcase (funcall fun cand) - (`(,_ ,_ ,suffix) suffix) - (ann ann))) - -(defun consult--read-affixate (fun cands) - "Affixate CANDS with annotation function FUN." - (mapcar (lambda (cand) - (let ((ann (funcall fun cand))) - (if (consp ann) - ann - (setq ann (or ann "")) - (list cand "" - ;; The default completion UI adds the - ;; `completions-annotations' face if no other faces are - ;; present. - (if (text-property-not-all 0 (length ann) 'face nil ann) - ann - (propertize ann 'face 'completions-annotations)))))) - cands)) - -(cl-defun consult--read-1 (table &key - prompt predicate require-match history default - keymap category initial narrow add-history annotate - state preview-key sort lookup group inherit-input-method) - "See `consult--read' for the documentation of the arguments." - (consult--minibuffer-with-setup-hook - (:append (lambda () - (add-hook 'after-change-functions #'consult--tofu-hide-in-minibuffer nil 'local) - (consult--setup-keymap keymap (consult--async-p table) narrow preview-key) - (setq-local minibuffer-default-add-function - (apply-partially #'consult--add-history (consult--async-p table) add-history)))) - (consult--with-async (async table) - (consult--with-preview - preview-key state - (lambda (narrow input cand) - (funcall lookup cand (funcall async nil) input narrow)) - (apply-partially #'run-hook-with-args-until-success - 'consult--completion-candidate-hook) - (pcase-exhaustive history - (`(:input ,var) var) - ((pred symbolp))) - ;; Do not unnecessarily let-bind the lambdas to avoid over-capturing in - ;; the interpreter. This will make closures and the lambda string - ;; representation larger, which makes debugging much worse. Fortunately - ;; the over-capturing problem does not affect the bytecode interpreter - ;; which does a proper scope analysis. - (let* ((metadata `(metadata - ,@(when category `((category . ,category))) - ,@(when group `((group-function . ,group))) - ,@(when annotate - `((affixation-function - . ,(apply-partially #'consult--read-affixate annotate)) - (annotation-function - . ,(apply-partially #'consult--read-annotate annotate)))) - ,@(unless sort '((cycle-sort-function . identity) - (display-sort-function . identity))))) - (consult--annotate-align-width 0) - (selected - (completing-read - prompt - (lambda (str pred action) - (let ((result (complete-with-action action (funcall async nil) str pred))) - (if (eq action 'metadata) - (if (and (eq (car result) 'metadata) (cdr result)) - ;; Merge metadata - `(metadata ,@(cdr metadata) ,@(cdr result)) - metadata) - result))) - predicate require-match initial - (if (symbolp history) history (cadr history)) - default - inherit-input-method))) - ;; Repair the null completion semantics. `completing-read' may return - ;; an empty string even if REQUIRE-MATCH is non-nil. One can always - ;; opt-in to null completion by passing the empty string for DEFAULT. - (when (and (eq require-match t) (not default) (equal selected "")) - (user-error "No selection")) - selected))))) - -(cl-defun consult--read (table &rest options &key - prompt predicate require-match history default - keymap category initial narrow add-history annotate - state preview-key sort lookup group inherit-input-method) - "Enhanced completing read function to select from TABLE. - -The function is a thin wrapper around `completing-read'. Keyword -arguments are used instead of positional arguments for code -clarity. On top of `completing-read' it additionally supports -computing the candidate list asynchronously, candidate preview -and narrowing. You should use `completing-read' instead of -`consult--read' if you don't use asynchronous candidate -computation or candidate preview. - -Keyword OPTIONS: - -PROMPT is the string which is shown as prompt in the minibuffer. -PREDICATE is a filter function called for each candidate, returns -nil or t. -REQUIRE-MATCH equals t means that an exact match is required. -HISTORY is the symbol of the history variable. -DEFAULT is the default selected value. -ADD-HISTORY is a list of items to add to the history. -CATEGORY is the completion category symbol. -SORT should be set to nil if the candidates are already sorted. -This will disable sorting in the completion UI. -LOOKUP is a lookup function passed the selected candidate string, -the list of candidates, the current input string and the current -narrowing value. -ANNOTATE is a function passed a candidate string. The function -should either return an annotation string or a list of three -strings (candidate prefix postfix). -INITIAL is the initial input string. -STATE is the state function, see `consult--with-preview'. -GROUP is a completion metadata `group-function' as documented in -the Elisp manual. -PREVIEW-KEY are the preview keys. Can be nil, `any', a single -key or a list of keys. -NARROW is an alist of narrowing prefix strings and description. -KEYMAP is a command-specific keymap. -INHERIT-INPUT-METHOD, if non-nil the minibuffer inherits the -input method." - ;; supported types - (cl-assert (or (functionp table) ;; dynamic table or asynchronous function - (obarrayp table) ;; obarray - (hash-table-p table) ;; hash table - (not table) ;; empty list - (stringp (car table)) ;; string list - (and (consp (car table)) (stringp (caar table))) ;; string alist - (and (consp (car table)) (symbolp (caar table))))) ;; symbol alist - (ignore prompt predicate require-match history default - keymap category initial narrow add-history annotate - state preview-key sort lookup group inherit-input-method) - (apply #'consult--read-1 table - (append - (consult--customize-get) - options - (list :prompt "Select: " - :preview-key consult-preview-key - :sort t - :lookup (lambda (selected &rest _) selected))))) - -;;;; Internal API: consult--prompt - -(cl-defun consult--prompt-1 (&key prompt history add-history initial default - keymap state preview-key transform inherit-input-method) - "See `consult--prompt' for documentation." - (consult--minibuffer-with-setup-hook - (:append (lambda () - (consult--setup-keymap keymap nil nil preview-key) - (setq-local minibuffer-default-add-function - (apply-partially #'consult--add-history nil add-history)))) - (consult--with-preview - preview-key state - (lambda (_narrow inp _cand) (funcall transform inp)) - (lambda () "") - history - (read-from-minibuffer prompt initial nil nil history default inherit-input-method)))) - -(cl-defun consult--prompt (&rest options &key prompt history add-history initial default - keymap state preview-key transform inherit-input-method) - "Read from minibuffer. - -Keyword OPTIONS: - -PROMPT is the string to prompt with. -TRANSFORM is a function which is applied to the current input string. -HISTORY is the symbol of the history variable. -INITIAL is initial input. -DEFAULT is the default selected value. -ADD-HISTORY is a list of items to add to the history. -STATE is the state function, see `consult--with-preview'. -PREVIEW-KEY are the preview keys (nil, `any', a single key or a list of keys). -KEYMAP is a command-specific keymap." - (ignore prompt history add-history initial default - keymap state preview-key transform inherit-input-method) - (apply #'consult--prompt-1 - (append - (consult--customize-get) - options - (list :prompt "Input: " - :preview-key consult-preview-key - :transform #'identity)))) - -;;;; Internal API: consult--multi - -(defsubst consult--multi-source (sources cand) - "Lookup source for CAND in SOURCES list." - (aref sources (consult--tofu-get cand))) - -(defun consult--multi-predicate (sources cand) - "Predicate function called for each candidate CAND given SOURCES." - (let* ((src (consult--multi-source sources cand)) - (narrow (plist-get src :narrow)) - (type (or (car-safe narrow) narrow -1))) - (or (eq consult--narrow type) - (not (or consult--narrow (plist-get src :hidden)))))) - -(defun consult--multi-narrow (sources) - "Return narrow list from SOURCES." - (thread-last sources - (mapcar (lambda (src) - (when-let (narrow (plist-get src :narrow)) - (if (consp narrow) - narrow - (when-let (name (plist-get src :name)) - (cons narrow name)))))) - (delq nil) - (delete-dups))) - -(defun consult--multi-annotate (sources cand) - "Annotate candidate CAND from multi SOURCES." - (consult--annotate-align - cand - (let ((src (consult--multi-source sources cand))) - (if-let ((fun (plist-get src :annotate))) - (funcall fun (cdr (get-text-property 0 'multi-category cand))) - (plist-get src :name))))) - -(defun consult--multi-group (sources cand transform) - "Return title of candidate CAND or TRANSFORM the candidate given SOURCES." - (if transform cand - (plist-get (consult--multi-source sources cand) :name))) - -(defun consult--multi-preview-key (sources) - "Return preview keys from SOURCES." - (list :predicate - (lambda (cand) - (if (plist-member (cdr cand) :preview-key) - (plist-get (cdr cand) :preview-key) - consult-preview-key)) - :keys - (delete-dups - (seq-mapcat (lambda (src) - (let ((key (if (plist-member src :preview-key) - (plist-get src :preview-key) - consult-preview-key))) - (ensure-list key))) - sources)))) - -(defun consult--multi-lookup (sources selected candidates _input narrow &rest _) - "Lookup SELECTED in CANDIDATES given SOURCES, with potential NARROW." - (if (or (string-blank-p selected) - (not (consult--tofu-p (aref selected (1- (length selected)))))) - ;; Non-existing candidate without Tofu or default submitted (empty string) - (let* ((src (cond - (narrow (seq-find (lambda (src) - (let ((n (plist-get src :narrow))) - (eq (or (car-safe n) n -1) narrow))) - sources)) - ((seq-find (lambda (src) (plist-get src :default)) sources)) - ((seq-find (lambda (src) (not (plist-get src :hidden))) sources)) - ((aref sources 0)))) - (idx (seq-position sources src)) - (def (and (string-blank-p selected) ;; default candidate - (seq-find (lambda (cand) (eq idx (consult--tofu-get cand))) candidates)))) - (if def - (cons (cdr (get-text-property 0 'multi-category def)) src) - `(,selected :match nil ,@src))) - (if-let (found (member selected candidates)) - ;; Existing candidate submitted - (cons (cdr (get-text-property 0 'multi-category (car found))) - (consult--multi-source sources selected)) - ;; Non-existing Tofu'ed candidate submitted, e.g., via Embark - `(,(substring selected 0 -1) :match nil ,@(consult--multi-source sources selected))))) - -(defun consult--multi-candidates (sources) - "Return `consult--multi' candidates from SOURCES." - (let ((idx 0) candidates) - (seq-doseq (src sources) - (let* ((face (and (plist-member src :face) `(face ,(plist-get src :face)))) - (cat (plist-get src :category)) - (items (plist-get src :items)) - (items (if (functionp items) (funcall items) items))) - (dolist (item items) - (let* ((str (or (car-safe item) item)) - (cand (consult--tofu-append str idx))) - ;; Preserve existing `multi-category' datum of the candidate. - (if (and (eq str item) (get-text-property 0 'multi-category str)) - (when face (add-text-properties 0 (length str) face cand)) - ;; Attach `multi-category' datum and face. - (add-text-properties - 0 (length str) - `(multi-category (,cat . ,(or (cdr-safe item) item)) ,@face) cand)) - (push cand candidates)))) - (cl-incf idx)) - (nreverse candidates))) - -(defun consult--multi-enabled-sources (sources) - "Return vector of enabled SOURCES." - (vconcat - (seq-filter (lambda (src) - (if-let (pred (plist-get src :enabled)) - (funcall pred) - t)) - (mapcar (lambda (src) - (if (symbolp src) (symbol-value src) src)) - sources)))) - -(defun consult--multi-state (sources) - "State function given SOURCES." - (when-let (states (delq nil (mapcar (lambda (src) - (when-let (fun (plist-get src :state)) - (cons src (funcall fun)))) - sources))) - (let (last-fun) - (pcase-lambda (action `(,cand . ,src)) - (pcase action - ('setup - (pcase-dolist (`(,_ . ,fun) states) - (funcall fun 'setup nil))) - ('exit - (pcase-dolist (`(,_ . ,fun) states) - (funcall fun 'exit nil))) - ('preview - (let ((selected-fun (cdr (assq src states)))) - ;; If the candidate source changed during preview communicate to - ;; the last source, that none of its candidates is previewed anymore. - (when (and last-fun (not (eq last-fun selected-fun))) - (funcall last-fun 'preview nil)) - (setq last-fun selected-fun) - (when selected-fun - (funcall selected-fun 'preview cand)))) - ('return - (let ((selected-fun (cdr (assq src states)))) - ;; Finish all the sources, except the selected one. - (pcase-dolist (`(,_ . ,fun) states) - (unless (eq fun selected-fun) - (funcall fun 'return nil))) - ;; Finish the source with the selected candidate - (when selected-fun - (funcall selected-fun 'return cand))))))))) - -(defun consult--multi (sources &rest options) - "Select from candidates taken from a list of SOURCES. - -OPTIONS is the plist of options passed to `consult--read'. The following -options are supported: :require-match, :history, :keymap, :initial, -:add-history, :sort and :inherit-input-method. The other options of -`consult--read' are used by the implementation of `consult--multi' and -should not be overwritten, except in in special scenarios. - -The function returns the selected candidate in the form (cons candidate -source-plist). The plist has the key :match with a value nil if the -candidate does not exist, t if the candidate exists and `new' if the -candidate has been created. The sources of the source list can either be -symbols of source variables or source values. Source values must be -plists with fields from the following list. - -Required source fields: -* :category - Completion category symbol. -* :items - List of strings to select from or function returning - list of strings. Note that the strings can use text properties - to carry metadata, which is then available to the :annotate, - :action and :state functions. - -Optional source fields: -* :name - Name of the source as a string, used for narrowing, - group titles and annotations. -* :narrow - Narrowing character or (character . string) pair. -* :enabled - Function which must return t if the source is enabled. -* :hidden - When t candidates of this source are hidden by default. -* :face - Face used for highlighting the candidates. -* :annotate - Annotation function called for each candidate, returns string. -* :history - Name of history variable to add selected candidate. -* :default - Must be t if the first item of the source is the default value. -* :action - Function called with the selected candidate. -* :new - Function called with new candidate name, only if :require-match is nil. -* :state - State constructor for the source, must return the - state function. The state function is informed about state - changes of the UI and can be used to implement preview. -* Other custom source fields can be added depending on the use - case. Note that the source is returned by `consult--multi' - together with the selected candidate." - (let* ((sources (consult--multi-enabled-sources sources)) - (candidates (consult--with-increased-gc - (consult--multi-candidates sources))) - (selected - (apply #'consult--read - candidates - (append - options - (list - :category 'multi-category - :predicate (apply-partially #'consult--multi-predicate sources) - :annotate (apply-partially #'consult--multi-annotate sources) - :group (apply-partially #'consult--multi-group sources) - :lookup (apply-partially #'consult--multi-lookup sources) - :preview-key (consult--multi-preview-key sources) - :narrow (consult--multi-narrow sources) - :state (consult--multi-state sources)))))) - (when-let (history (plist-get (cdr selected) :history)) - (add-to-history history (car selected))) - (if (plist-member (cdr selected) :match) - (when-let (fun (plist-get (cdr selected) :new)) - (funcall fun (car selected)) - (plist-put (cdr selected) :match 'new)) - (when-let (fun (plist-get (cdr selected) :action)) - (funcall fun (car selected))) - (setq selected `(,(car selected) :match t ,@(cdr selected)))) - selected)) - -;;;; Customization macro - -(defun consult--customize-put (cmds prop form) - "Set property PROP to FORM of commands CMDS." - (dolist (cmd cmds) - (cond - ((and (boundp cmd) (consp (symbol-value cmd))) - (setf (plist-get (symbol-value cmd) prop) (eval form 'lexical))) - ((functionp cmd) - (setf (plist-get (alist-get cmd consult--customize-alist) prop) form)) - (t (user-error "%s is neither a Command command nor a source" cmd)))) - nil) - -(defmacro consult-customize (&rest args) - "Set properties of commands or sources. -ARGS is a list of commands or sources followed by the list of -keyword-value pairs. For `consult-customize' to succeed, the -customized sources and commands must exist. When a command is -invoked, the value of `this-command' is used to lookup the -corresponding customization options." - (let (setter) - (while args - (let ((cmds (seq-take-while (lambda (x) (not (keywordp x))) args))) - (setq args (seq-drop-while (lambda (x) (not (keywordp x))) args)) - (while (keywordp (car args)) - (push `(consult--customize-put ',cmds ,(car args) ',(cadr args)) setter) - (setq args (cddr args))))) - (macroexp-progn setter))) - -(defun consult--customize-get () - "Get configuration from `consult--customize-alist' for `this-command'." - (mapcar (lambda (x) (eval x 'lexical)) - (alist-get this-command consult--customize-alist))) - -;;;; Commands - -;;;;; Command: consult-completion-in-region - -(defun consult--insertion-preview (start end) - "State function for previewing a candidate in a specific region. -The candidates are previewed in the region from START to END. This function is -used as the `:state' argument for `consult--read' in the `consult-yank' family -of functions and in `consult-completion-in-region'." - (unless (or (minibufferp) - ;; Disable preview if anything odd is going on with the markers. - ;; Otherwise we get "Marker points into wrong buffer errors". See - ;; gh:minad/consult#375, where Org mode source blocks are - ;; completed in a different buffer than the original buffer. This - ;; completion is probably also problematic in my Corfu completion - ;; package. - (not (eq (window-buffer) (current-buffer))) - (and (markerp start) (not (eq (marker-buffer start) (current-buffer)))) - (and (markerp end) (not (eq (marker-buffer end) (current-buffer))))) - (let (ov) - (lambda (action cand) - (cond - ((and (not cand) ov) - (delete-overlay ov) - (setq ov nil)) - ((and (eq action 'preview) cand) - (unless ov - (setq ov (consult--make-overlay start end - 'invisible t - 'window (selected-window)))) - ;; Use `add-face-text-property' on a copy of "cand in order to merge face properties - (setq cand (copy-sequence cand)) - (add-face-text-property 0 (length cand) 'consult-preview-insertion t cand) - ;; Use the `before-string' property since the overlay might be empty. - (overlay-put ov 'before-string cand))))))) - -;;;###autoload -(defun consult-completion-in-region (start end collection &optional predicate) - "Use minibuffer completion as the UI for `completion-at-point'. - -The function is called with 4 arguments: START END COLLECTION -PREDICATE. The arguments and expected return value are as -specified for `completion-in-region'. Use this function as a -value for `completion-in-region-function'." - (barf-if-buffer-read-only) - (let* ((initial (buffer-substring-no-properties start end)) - (metadata (completion-metadata initial collection predicate)) - ;; TODO: `minibuffer-completing-file-name' is mostly deprecated, but - ;; still in use. Packages should instead use the completion metadata. - (minibuffer-completing-file-name - (eq 'file (completion-metadata-get metadata 'category))) - (threshold (completion--cycle-threshold metadata)) - (all (completion-all-completions initial collection predicate (length initial))) - ;; Wrap all annotation functions to ensure that they are executed - ;; in the original buffer. - (exit-fun (plist-get completion-extra-properties :exit-function)) - (ann-fun (plist-get completion-extra-properties :annotation-function)) - (aff-fun (plist-get completion-extra-properties :affixation-function)) - (docsig-fun (plist-get completion-extra-properties :company-docsig)) - (completion-extra-properties - `(,@(and ann-fun (list :annotation-function (consult--in-buffer ann-fun))) - ,@(and aff-fun (list :affixation-function (consult--in-buffer aff-fun))) - ;; Provide `:annotation-function' if `:company-docsig' is specified. - ,@(and docsig-fun (not ann-fun) (not aff-fun) - (list :annotation-function - (consult--in-buffer - (lambda (cand) - (concat (propertize " " 'display '(space :align-to center)) - (funcall docsig-fun cand))))))))) - ;; error if `threshold' is t or the improper list `all' is too short - (if (and threshold - (or (not (consp (ignore-errors (nthcdr threshold all)))) - (and completion-cycling completion-all-sorted-completions))) - (completion--in-region start end collection predicate) - (let* ((limit (car (completion-boundaries initial collection predicate ""))) - (this-command #'consult-completion-in-region) - (completion - (cond - ((atom all) nil) - ((and (consp all) (atom (cdr all))) - (concat (substring initial 0 limit) (car all))) - (t - (consult--local-let ((enable-recursive-minibuffers t)) - ;; Evaluate completion table in the original buffer. - ;; This is a reasonable thing to do and required by - ;; some completion tables in particular by lsp-mode. - ;; See gh:minad/vertico#61. - (consult--read (consult--completion-table-in-buffer collection) - :prompt "Completion: " - :state (consult--insertion-preview start end) - :predicate predicate - :initial initial)))))) - (if completion - (progn - ;; bug#55205: completion--replace removes properties! - (completion--replace start end (setq completion (concat completion))) - (when exit-fun - (funcall exit-fun completion - ;; If completion is finished and cannot be further - ;; completed, return `finished'. Otherwise return - ;; `exact'. - (if (eq (try-completion completion collection predicate) t) - 'finished 'exact))) - t) - (message "No completion") - nil))))) - -;;;;; Command: consult-outline - -(defun consult--outline-candidates () - "Return alist of outline headings and positions." - (consult--forbid-minibuffer) - (let* ((line (line-number-at-pos (point-min) consult-line-numbers-widen)) - (heading-regexp (concat "^\\(?:" - ;; default definition from outline.el - (or (bound-and-true-p outline-regexp) "[*\^L]+") - "\\)")) - (heading-alist (bound-and-true-p outline-heading-alist)) - (level-fun (or (bound-and-true-p outline-level) - (lambda () ;; as in the default from outline.el - (or (cdr (assoc (match-string 0) heading-alist)) - (- (match-end 0) (match-beginning 0)))))) - (buffer (current-buffer)) - candidates) - (save-excursion - (goto-char (point-min)) - (while (save-excursion - (if-let (fun (bound-and-true-p outline-search-function)) - (funcall fun) - (re-search-forward heading-regexp nil t))) - (cl-incf line (consult--count-lines (match-beginning 0))) - (push (consult--location-candidate - (consult--buffer-substring (pos-bol) (pos-eol) 'fontify) - (cons buffer (point)) (1- line) (1- line) - 'consult--outline-level (funcall level-fun)) - candidates) - (goto-char (1+ (pos-eol))))) - (unless candidates - (user-error "No headings")) - (nreverse candidates))) - -;;;###autoload -(defun consult-outline (&optional level) - "Jump to an outline heading, obtained by matching against `outline-regexp'. - -This command supports narrowing to a heading level and candidate -preview. The initial narrowing LEVEL can be given as prefix -argument. The symbol at point is added to the future history." - (interactive - (list (and current-prefix-arg (prefix-numeric-value current-prefix-arg)))) - (let* ((candidates (consult--slow-operation - "Collecting headings..." - (consult--outline-candidates))) - (min-level (- (cl-loop for cand in candidates minimize - (get-text-property 0 'consult--outline-level cand)) - ?1)) - (narrow-pred (lambda (cand) - (<= (get-text-property 0 'consult--outline-level cand) - (+ consult--narrow min-level)))) - (narrow-keys (mapcar (lambda (c) (cons c (format "Level %c" c))) - (number-sequence ?1 ?9))) - (narrow-init (and level (max ?1 (min ?9 (+ level ?0)))))) - (consult--read - candidates - :prompt "Go to heading: " - :annotate (consult--line-prefix) - :category 'consult-location - :sort nil - :require-match t - :lookup #'consult--line-match - :narrow `(:predicate ,narrow-pred :keys ,narrow-keys :initial ,narrow-init) - :history '(:input consult--line-history) - :add-history (thing-at-point 'symbol) - :state (consult--location-state candidates)))) - -;;;;; Command: consult-mark - -(defun consult--mark-candidates (markers) - "Return list of candidates strings for MARKERS." - (consult--forbid-minibuffer) - (let ((candidates) - (current-buf (current-buffer))) - (save-excursion - (dolist (marker markers) - (when-let ((pos (marker-position marker)) - (buf (marker-buffer marker))) - (when (and (eq buf current-buf) - (consult--in-range-p pos)) - (goto-char pos) - ;; `line-number-at-pos' is a very slow function, which should be - ;; replaced everywhere. However in this case the slow - ;; line-number-at-pos does not hurt much, since the mark ring is - ;; usually small since it is limited by `mark-ring-max'. - (push (consult--location-candidate - (consult--line-with-mark marker) marker - (line-number-at-pos pos consult-line-numbers-widen) - marker) - candidates))))) - (unless candidates - (user-error "No marks")) - (nreverse (delete-dups candidates)))) - -;;;###autoload -(defun consult-mark (&optional markers) - "Jump to a marker in MARKERS list (defaults to buffer-local `mark-ring'). - -The command supports preview of the currently selected marker position. -The symbol at point is added to the future history." - (interactive) - (consult--read - (consult--mark-candidates - (or markers (cons (mark-marker) mark-ring))) - :prompt "Go to mark: " - :annotate (consult--line-prefix) - :category 'consult-location - :sort nil - :require-match t - :lookup #'consult--lookup-location - :history '(:input consult--line-history) - :add-history (thing-at-point 'symbol) - :state (consult--jump-state))) - -;;;;; Command: consult-global-mark - -(defun consult--global-mark-candidates (markers) - "Return list of candidates strings for MARKERS." - (consult--forbid-minibuffer) - (let ((candidates)) - (save-excursion - (dolist (marker markers) - (when-let ((pos (marker-position marker)) - (buf (marker-buffer marker))) - (unless (minibufferp buf) - (with-current-buffer buf - (when (consult--in-range-p pos) - (goto-char pos) - ;; `line-number-at-pos' is slow, see comment in `consult--mark-candidates'. - (let* ((line (line-number-at-pos pos consult-line-numbers-widen)) - (prefix (consult--format-file-line-match (buffer-name buf) line "")) - (cand (concat prefix (consult--line-with-mark marker) (consult--tofu-encode marker)))) - (put-text-property 0 (length prefix) 'consult-strip t cand) - (put-text-property 0 (length cand) 'consult-location (cons marker line) cand) - (push cand candidates)))))))) - (unless candidates - (user-error "No global marks")) - (nreverse (delete-dups candidates)))) - -;;;###autoload -(defun consult-global-mark (&optional markers) - "Jump to a marker in MARKERS list (defaults to `global-mark-ring'). - -The command supports preview of the currently selected marker position. -The symbol at point is added to the future history." - (interactive) - (consult--read - (consult--global-mark-candidates - (or markers global-mark-ring)) - :prompt "Go to global mark: " - ;; Despite `consult-global-mark' formatting the candidates in grep-like - ;; style, we are not using the `consult-grep' category, since the candidates - ;; have location markers attached. - :category 'consult-location - :sort nil - :require-match t - :lookup #'consult--lookup-location - :history '(:input consult--line-history) - :add-history (thing-at-point 'symbol) - :state (consult--jump-state))) - -;;;;; Command: consult-line - -(defun consult--line-candidates (top curr-line) - "Return list of line candidates. -Start from top if TOP non-nil. -CURR-LINE is the current line number." - (consult--forbid-minibuffer) - (consult--fontify-all) - (let* ((buffer (current-buffer)) - (line (line-number-at-pos (point-min) consult-line-numbers-widen)) - default-cand candidates) - (consult--each-line beg end - (unless (looking-at-p "^\\s-*$") - (push (consult--location-candidate - (consult--buffer-substring beg end) - (cons buffer beg) line line) - candidates) - (when (and (not default-cand) (>= line curr-line)) - (setq default-cand candidates))) - (cl-incf line)) - (unless candidates - (user-error "No lines")) - (nreverse - (if (or top (not default-cand)) - candidates - (let ((before (cdr default-cand))) - (setcdr default-cand nil) - (nconc before candidates)))))) - -(defun consult--line-point-placement (selected candidates highlighted &rest ignored-faces) - "Find point position on matching line. -SELECTED is the currently selected candidate. -CANDIDATES is the list of candidates. -HIGHLIGHTED is the highlighted string to determine the match position. -IGNORED-FACES are ignored when determining the match position." - (when-let (pos (consult--lookup-location selected candidates)) - (if highlighted - (let* ((matches (apply #'consult--point-placement highlighted 0 ignored-faces)) - (dest (+ pos (car matches)))) - ;; Only create a new marker when jumping across buffers (for example - ;; `consult-line-multi'). Avoid creating unnecessary markers, when - ;; scrolling through candidates, since creating markers is not free. - (when (and (markerp pos) (not (eq (marker-buffer pos) (current-buffer)))) - (setq dest (move-marker (make-marker) dest (marker-buffer pos)))) - (cons dest (cdr matches))) - pos))) - -(defun consult--line-match (selected candidates input &rest _) - "Lookup position of match. -SELECTED is the currently selected candidate. -CANDIDATES is the list of candidates. -INPUT is the input string entered by the user." - (consult--line-point-placement selected candidates - (and (not (string-blank-p input)) - (car (consult--completion-filter - input - (list (substring-no-properties selected)) - 'consult-location 'highlight))) - 'completions-first-difference)) - -;;;###autoload -(defun consult-line (&optional initial start) - "Search for a matching line. - -Depending on the setting `consult-point-placement' the command -jumps to the beginning or the end of the first match on the line -or the line beginning. The default candidate is the non-empty -line next to point. This command obeys narrowing. Optional -INITIAL input can be provided. The search starting point is -changed if the START prefix argument is set. The symbol at point -and the last `isearch-string' is added to the future history." - (interactive (list nil (not (not current-prefix-arg)))) - (let* ((curr-line (line-number-at-pos (point) consult-line-numbers-widen)) - (top (not (eq start consult-line-start-from-top))) - (candidates (consult--slow-operation "Collecting lines..." - (consult--line-candidates top curr-line)))) - (consult--read - candidates - :prompt (if top "Go to line from top: " "Go to line: ") - :annotate (consult--line-prefix curr-line) - :category 'consult-location - :sort nil - :require-match t - ;; Always add last `isearch-string' to future history - :add-history (list (thing-at-point 'symbol) isearch-string) - :history '(:input consult--line-history) - :lookup #'consult--line-match - :default (car candidates) - ;; Add `isearch-string' as initial input if starting from Isearch - :initial (or initial - (and isearch-mode - (prog1 isearch-string (isearch-done)))) - :state (consult--location-state candidates)))) - -;;;;; Command: consult-line-multi - -(defun consult--line-multi-match (selected candidates &rest _) - "Lookup position of match. -SELECTED is the currently selected candidate. -CANDIDATES is the list of candidates." - (consult--line-point-placement selected candidates - (car (member selected candidates)))) - -(defun consult--line-multi-group (cand transform) - "Group function used by `consult-line-multi'. -If TRANSFORM non-nil, return transformed CAND, otherwise return title." - (if transform cand - (let* ((marker (car (get-text-property 0 'consult-location cand))) - (buf (if (consp marker) - (car marker) ;; Handle cheap marker - (marker-buffer marker)))) - (if buf (buffer-name buf) "Dead buffer")))) - -(defun consult--line-multi-candidates (buffers input) - "Collect matching candidates from multiple buffers. -INPUT is the user input which should be matched. -BUFFERS is the list of buffers." - (pcase-let ((`(,regexps . ,hl) - (funcall consult--regexp-compiler - input 'emacs completion-ignore-case)) - (candidates nil) - (cand-idx 0)) - (save-match-data - (dolist (buf buffers (nreverse candidates)) - (with-current-buffer buf - (save-excursion - (let ((line (line-number-at-pos (point-min) consult-line-numbers-widen))) - (goto-char (point-min)) - (while (and (not (eobp)) - (save-excursion (re-search-forward (car regexps) nil t))) - (cl-incf line (consult--count-lines (match-beginning 0))) - (let ((bol (pos-bol)) - (eol (pos-eol))) - (goto-char bol) - (when (and (not (looking-at-p "^\\s-*$")) - (seq-every-p (lambda (r) - (goto-char bol) - (re-search-forward r eol t)) - (cdr regexps))) - (push (consult--location-candidate - (funcall hl (buffer-substring-no-properties bol eol)) - (cons buf bol) (1- line) cand-idx) - candidates) - (cl-incf cand-idx)) - (goto-char (1+ eol))))))))))) - -;;;###autoload -(defun consult-line-multi (query &optional initial) - "Search for a matching line in multiple buffers. - -By default search across all project buffers. If the prefix -argument QUERY is non-nil, all buffers are searched. Optional -INITIAL input can be provided. The symbol at point and the last -`isearch-string' is added to the future history. In order to -search a subset of buffers, QUERY can be set to a plist according -to `consult--buffer-query'." - (interactive "P") - (unless (keywordp (car-safe query)) - (setq query (list :sort 'alpha-current :directory (and (not query) 'project)))) - (pcase-let* ((`(,prompt . ,buffers) (consult--buffer-query-prompt "Go to line" query)) - (collection (consult--dynamic-collection - (apply-partially #'consult--line-multi-candidates - buffers)))) - (consult--read - collection - :prompt prompt - :annotate (consult--line-prefix) - :category 'consult-location - :sort nil - :require-match t - ;; Always add last Isearch string to future history - :add-history (mapcar #'consult--async-split-initial - (delq nil (list (thing-at-point 'symbol) - isearch-string))) - :history '(:input consult--line-multi-history) - :lookup #'consult--line-multi-match - ;; Add `isearch-string' as initial input if starting from Isearch - :initial (consult--async-split-initial - (or initial - (and isearch-mode - (prog1 isearch-string (isearch-done))))) - :state (consult--location-state (lambda () (funcall collection nil))) - :group #'consult--line-multi-group))) - -;;;;; Command: consult-keep-lines - -(defun consult--keep-lines-state (filter) - "State function for `consult-keep-lines' with FILTER function." - (let ((font-lock-orig font-lock-mode) - (whitespace-orig (bound-and-true-p whitespace-mode)) - (hl-line-orig (bound-and-true-p hl-line-mode)) - (point-orig (point)) - lines content-orig replace last-input) - (if (use-region-p) - (save-restriction - ;; Use the same behavior as `keep-lines'. - (let ((rbeg (region-beginning)) - (rend (save-excursion - (goto-char (region-end)) - (unless (or (bolp) (eobp)) - (forward-line 0)) - (point)))) - (consult--fontify-region rbeg rend) - (narrow-to-region rbeg rend) - (consult--each-line beg end - (push (consult--buffer-substring beg end) lines)) - (setq content-orig (buffer-string) - replace (lambda (content &optional pos) - (delete-region rbeg rend) - (insert-before-markers content) - (goto-char (or pos rbeg)) - (setq rend (+ rbeg (length content))) - (add-face-text-property rbeg rend 'region t))))) - (consult--fontify-all) - (setq content-orig (buffer-string) - replace (lambda (content &optional pos) - (delete-region (point-min) (point-max)) - (insert content) - (goto-char (or pos (point-min))))) - (consult--each-line beg end - (push (consult--buffer-substring beg end) lines))) - (setq lines (nreverse lines)) - (lambda (action input) - ;; Restoring content and point position - (when (and (eq action 'return) last-input) - ;; No undo recording, modification hooks, buffer modified-status - (with-silent-modifications (funcall replace content-orig point-orig))) - ;; Committing or new input provided -> Update - (when (and input ;; Input has been provided - (or - ;; Committing, but not with empty input - (and (eq action 'return) (not (string-match-p "\\`!? ?\\'" input))) - ;; Input has changed - (not (equal input last-input)))) - (let ((filtered-content - (if (string-match-p "\\`!? ?\\'" input) - ;; Special case the empty input for performance. - ;; Otherwise it could happen that the minibuffer is empty, - ;; but the buffer has not been updated. - content-orig - (if (eq action 'return) - (apply #'concat (mapcan (lambda (x) (list x "\n")) - (funcall filter input lines))) - (while-no-input - ;; Heavy computation is interruptible if *not* committing! - ;; Allocate new string candidates since the matching function mutates! - (apply #'concat (mapcan (lambda (x) (list x "\n")) - (funcall filter input (mapcar #'copy-sequence lines))))))))) - (when (stringp filtered-content) - (when font-lock-mode (font-lock-mode -1)) - (when (bound-and-true-p whitespace-mode) (whitespace-mode -1)) - (when (bound-and-true-p hl-line-mode) (hl-line-mode -1)) - (if (eq action 'return) - (atomic-change-group - ;; Disable modification hooks for performance - (let ((inhibit-modification-hooks t)) - (funcall replace filtered-content))) - ;; No undo recording, modification hooks, buffer modified-status - (with-silent-modifications - (funcall replace filtered-content) - (setq last-input input)))))) - ;; Restore modes - (when (eq action 'return) - (when hl-line-orig (hl-line-mode 1)) - (when whitespace-orig (whitespace-mode 1)) - (when font-lock-orig (font-lock-mode 1)))))) - -;;;###autoload -(defun consult-keep-lines (filter &optional initial) - "Select a subset of the lines in the current buffer with live preview. - -The selected lines are kept and the other lines are deleted. When called -interactively, the lines selected are those that match the minibuffer input. In -order to match the inverse of the input, prefix the input with `! '. When -called from Elisp, the filtering is performed by a FILTER function. This -command obeys narrowing. - -FILTER is the filter function. -INITIAL is the initial input." - (interactive - (list (lambda (pattern cands) - ;; Use consult-location completion category when filtering lines - (consult--completion-filter-dispatch - pattern cands 'consult-location 'highlight)))) - (consult--forbid-minibuffer) - (let ((ro buffer-read-only)) - (unwind-protect - (consult--minibuffer-with-setup-hook - (lambda () - (when ro - (minibuffer-message - (substitute-command-keys - " [Unlocked read-only buffer. \\[minibuffer-keyboard-quit] to quit.]")))) - (setq buffer-read-only nil) - (consult--with-increased-gc - (consult--prompt - :prompt "Keep lines: " - :initial initial - :history 'consult--line-history - :state (consult--keep-lines-state filter)))) - (setq buffer-read-only ro)))) - -;;;;; Command: consult-focus-lines - -(defun consult--focus-lines-state (filter) - "State function for `consult-focus-lines' with FILTER function." - (let (lines overlays last-input pt-orig pt-min pt-max) - (save-excursion - (save-restriction - (if (not (use-region-p)) - (consult--fontify-all) - (consult--fontify-region (region-beginning) (region-end)) - (narrow-to-region - (region-beginning) - ;; Behave the same as `keep-lines'. - ;; Move to the next line. - (save-excursion - (goto-char (region-end)) - (unless (or (bolp) (eobp)) - (forward-line 0)) - (point)))) - (setq pt-orig (point) pt-min (point-min) pt-max (point-max)) - (let ((i 0)) - (consult--each-line beg end - ;; Use "\n" for empty lines, since we need a non-empty string to - ;; attach the text property to. - (let ((line (if (eq beg end) (char-to-string ?\n) - (buffer-substring-no-properties beg end)))) - (put-text-property 0 1 'consult--focus-line (cons (cl-incf i) beg) line) - (push line lines))) - (setq lines (nreverse lines))))) - (lambda (action input) - ;; New input provided -> Update - (when (and input (not (equal input last-input))) - (let (new-overlays) - (pcase (while-no-input - (unless (string-match-p "\\`!? ?\\'" input) ;; Empty input. - (let* ((inhibit-quit (eq action 'return)) ;; Non interruptible, when quitting! - (not (string-prefix-p "! " input)) - (stripped (string-remove-prefix "! " input)) - (matches (funcall filter stripped lines)) - (old-ind 0) - (block-beg pt-min) - (block-end pt-min)) - (while old-ind - (let ((match (pop matches)) (ind nil) (beg pt-max) (end pt-max) prop) - (when match - (setq prop (get-text-property 0 'consult--focus-line match) - ind (car prop) - beg (cdr prop) - ;; Check for empty lines, see above. - end (+ 1 beg (if (equal match "\n") 0 (length match))))) - (unless (eq ind (1+ old-ind)) - (let ((a (if not block-beg block-end)) - (b (if not block-end beg))) - (when (/= a b) - (push (consult--make-overlay a b 'invisible t) new-overlays))) - (setq block-beg beg)) - (setq block-end end old-ind ind))))) - 'commit) - ('commit - (mapc #'delete-overlay overlays) - (setq last-input input overlays new-overlays)) - (_ (mapc #'delete-overlay new-overlays))))) - (when (eq action 'return) - (cond - ((not input) - (mapc #'delete-overlay overlays) - (goto-char pt-orig)) - ((equal input "") - (consult-focus-lines nil 'show) - (goto-char pt-orig)) - (t - ;; Successfully terminated -> Remember invisible overlays - (setq consult--focus-lines-overlays - (nconc consult--focus-lines-overlays overlays)) - ;; move point past invisible - (goto-char (if-let (ov (and (invisible-p pt-orig) - (seq-find (lambda (ov) (overlay-get ov 'invisible)) - (overlays-at pt-orig)))) - (overlay-end ov) - pt-orig)))))))) - -;;;###autoload -(defun consult-focus-lines (filter &optional show initial) - "Hide or show lines using overlays. - -The selected lines are shown and the other lines hidden. When called -interactively, the lines selected are those that match the minibuffer input. In -order to match the inverse of the input, prefix the input with `! '. With -optional prefix argument SHOW reveal the hidden lines. Alternatively the -command can be restarted to reveal the lines. When called from Elisp, the -filtering is performed by a FILTER function. This command obeys narrowing. - -FILTER is the filter function. -INITIAL is the initial input." - (interactive - (list (lambda (pattern cands) - ;; Use consult-location completion category when filtering lines - (consult--completion-filter-dispatch - pattern cands 'consult-location nil)) - current-prefix-arg)) - (if show - (progn - (mapc #'delete-overlay consult--focus-lines-overlays) - (setq consult--focus-lines-overlays nil) - (message "All lines revealed")) - (consult--forbid-minibuffer) - (consult--with-increased-gc - (consult--prompt - :prompt - (if consult--focus-lines-overlays - "Focus on lines (RET to reveal): " - "Focus on lines: ") - :initial initial - :history 'consult--line-history - :state (consult--focus-lines-state filter))))) - -;;;;; Command: consult-goto-line - -(defun consult--goto-line-position (str msg) - "Transform input STR to line number. -Print an error message with MSG function." - (save-match-data - (if (and str (string-match "\\`\\([[:digit:]]+\\):?\\([[:digit:]]*\\)\\'" str)) - (let ((line (string-to-number (match-string 1 str))) - (col (string-to-number (match-string 2 str)))) - (save-excursion - (save-restriction - (when consult-line-numbers-widen - (widen)) - (goto-char (point-min)) - (forward-line (1- line)) - (goto-char (min (+ (point) col) (pos-eol))) - (point)))) - (when (and str (not (equal str ""))) - (funcall msg "Please enter a number.")) - nil))) - -;;;###autoload -(defun consult-goto-line (&optional arg) - "Read line number and jump to the line with preview. - -Enter either a line number to jump to the first column of the -given line or line:column in order to jump to a specific column. -Jump directly if a line number is given as prefix ARG. The -command respects narrowing and the settings -`consult-goto-line-numbers' and `consult-line-numbers-widen'." - (interactive "P") - (if arg - (call-interactively #'goto-line) - (consult--forbid-minibuffer) - (consult--local-let ((display-line-numbers consult-goto-line-numbers) - (display-line-numbers-widen consult-line-numbers-widen)) - (while (if-let (pos (consult--goto-line-position - (consult--prompt - :prompt "Go to line: " - :history 'goto-line-history - :state - (let ((preview (consult--jump-preview))) - (lambda (action str) - (funcall preview action - (consult--goto-line-position str #'ignore))))) - #'minibuffer-message)) - (consult--jump pos) - t))))) - -;;;;; Command: consult-recent-file - -(defun consult--file-preview () - "Create preview function for files." - (let ((open (consult--temporary-files)) - (preview (consult--buffer-preview))) - (lambda (action cand) - (unless cand - (funcall open)) - (funcall preview action - (and cand - (eq action 'preview) - (funcall open cand)))))) - -(defun consult--file-action (file) - "Open FILE via `consult--buffer-action'." - ;; Try to preserve the buffer as is, if it has already been opened, for - ;; example in literal or raw mode. - (setq file (abbreviate-file-name (expand-file-name file))) - (consult--buffer-action (or (get-file-buffer file) (find-file-noselect file)))) - -(consult--define-state file) - -;;;###autoload -(defun consult-recent-file () - "Find recent file using `completing-read'." - (interactive) - (find-file - (consult--read - (or - (mapcar #'consult--fast-abbreviate-file-name (bound-and-true-p recentf-list)) - (user-error "No recent files, `recentf-mode' is %s" - (if recentf-mode "enabled" "disabled"))) - :prompt "Find recent file: " - :sort nil - :require-match t - :category 'file - :state (consult--file-preview) - :history 'file-name-history))) - -;;;;; Command: consult-mode-command - -(defun consult--mode-name (mode) - "Return name part of MODE." - (replace-regexp-in-string - "global-\\(.*\\)-mode" "\\1" - (replace-regexp-in-string - "\\(-global\\)?-mode\\'" "" - (if (eq mode 'c-mode) - "cc" - (symbol-name mode)) - 'fixedcase) - 'fixedcase)) - -(defun consult--mode-command-candidates (modes) - "Extract commands from MODES. - -The list of features is searched for files belonging to the modes. -From these files, the commands are extracted." - (let* ((case-fold-search) - (buffer (current-buffer)) - (command-filter (consult--regexp-filter (seq-filter #'stringp consult-mode-command-filter))) - (feature-filter (seq-filter #'symbolp consult-mode-command-filter)) - (minor-hash (consult--string-hash minor-mode-list)) - (minor-local-modes (seq-filter (lambda (m) - (and (gethash m minor-hash) - (local-variable-if-set-p m))) - modes)) - (minor-global-modes (seq-filter (lambda (m) - (and (gethash m minor-hash) - (not (local-variable-if-set-p m)))) - modes)) - (major-modes (seq-remove (lambda (m) - (gethash m minor-hash)) - modes)) - (major-paths-hash (consult--string-hash (mapcar #'symbol-file major-modes))) - (minor-local-paths-hash (consult--string-hash (mapcar #'symbol-file minor-local-modes))) - (minor-global-paths-hash (consult--string-hash (mapcar #'symbol-file minor-global-modes))) - (major-name-regexp (regexp-opt (mapcar #'consult--mode-name major-modes))) - (minor-local-name-regexp (regexp-opt (mapcar #'consult--mode-name minor-local-modes))) - (minor-global-name-regexp (regexp-opt (mapcar #'consult--mode-name minor-global-modes))) - (commands)) - (dolist (feature load-history commands) - (when-let (name (alist-get 'provide feature)) - (let* ((path (car feature)) - (file (file-name-nondirectory path)) - (key (cond - ((memq name feature-filter) nil) - ((or (gethash path major-paths-hash) - (string-match-p major-name-regexp file)) - ?m) - ((or (gethash path minor-local-paths-hash) - (string-match-p minor-local-name-regexp file)) - ?l) - ((or (gethash path minor-global-paths-hash) - (string-match-p minor-global-name-regexp file)) - ?g)))) - (when key - (dolist (cmd (cdr feature)) - (let ((sym (cdr-safe cmd))) - (when (and (consp cmd) - (eq (car cmd) 'defun) - (commandp sym) - (not (get sym 'byte-obsolete-info)) - ;; Emacs 28 has a `read-extended-command-predicate' - (if (bound-and-true-p read-extended-command-predicate) - (funcall read-extended-command-predicate sym buffer) - t)) - (let ((name (symbol-name sym))) - (unless (string-match-p command-filter name) - (push (propertize name - 'consult--candidate sym - 'consult--type key) - commands)))))))))))) - -;;;###autoload -(defun consult-mode-command (&rest modes) - "Run a command from any of the given MODES. - -If no MODES are specified, use currently active major and minor modes." - (interactive) - (unless modes - (setq modes (cons major-mode - (seq-filter (lambda (m) - (and (boundp m) (symbol-value m))) - minor-mode-list)))) - (let ((narrow `((?m . ,(format "Major: %s" major-mode)) - (?l . "Local Minor") - (?g . "Global Minor")))) - (command-execute - (consult--read - (consult--mode-command-candidates modes) - :prompt "Mode command: " - :predicate - (lambda (cand) - (let ((key (get-text-property 0 'consult--type cand))) - (if consult--narrow - (= key consult--narrow) - (/= key ?g)))) - :lookup #'consult--lookup-candidate - :group (consult--type-group narrow) - :narrow narrow - :require-match t - :history 'extended-command-history - :category 'command)))) - -;;;;; Command: consult-yank - -(defun consult--read-from-kill-ring () - "Open kill ring menu and return selected string." - ;; `current-kill' updates `kill-ring' with interprogram paste, see - ;; gh:minad/consult#443. - (current-kill 0) - ;; Do not specify a :lookup function in order to preserve completion-styles - ;; highlighting of the current candidate. We have to perform a final lookup to - ;; obtain the original candidate which may be propertized with yank-specific - ;; properties, like 'yank-handler. - (consult--lookup-member - (consult--read - (consult--remove-dups - (or (if consult-yank-rotate - (append kill-ring-yank-pointer - (butlast kill-ring (length kill-ring-yank-pointer))) - kill-ring) - (user-error "Kill ring is empty"))) - :prompt "Yank from kill-ring: " - :history t ;; disable history - :sort nil - :category 'kill-ring - :require-match t - :state - (consult--insertion-preview - (point) - ;; If previous command is yank, hide previously yanked string - (or (and (eq last-command 'yank) (mark t)) (point)))) - kill-ring)) - -;; Adapted from the Emacs `yank-from-kill-ring' function. -;;;###autoload -(defun consult-yank-from-kill-ring (string &optional arg) - "Select STRING from the kill ring and insert it. -With prefix ARG, put point at beginning, and mark at end, like `yank' does. - -This command behaves like `yank-from-kill-ring' in Emacs 28, which also offers -a `completing-read' interface to the `kill-ring'. Additionally the Consult -version supports preview of the selected string." - (interactive (list (consult--read-from-kill-ring) current-prefix-arg)) - (when string - (setq yank-window-start (window-start)) - (push-mark) - (insert-for-yank string) - (setq this-command 'yank) - (when consult-yank-rotate - (if-let (pos (seq-position kill-ring string)) - (setq kill-ring-yank-pointer (nthcdr pos kill-ring)) - (kill-new string))) - (when (consp arg) - ;; Swap point and mark like in `yank'. - (goto-char (prog1 (mark t) - (set-marker (mark-marker) (point) (current-buffer))))))) - -(put 'consult-yank-replace 'delete-selection 'yank) -(put 'consult-yank-pop 'delete-selection 'yank) -(put 'consult-yank-from-kill-ring 'delete-selection 'yank) - -;;;###autoload -(defun consult-yank-pop (&optional arg) - "If there is a recent yank act like `yank-pop'. - -Otherwise select string from the kill ring and insert it. -See `yank-pop' for the meaning of ARG. - -This command behaves like `yank-pop' in Emacs 28, which also offers a -`completing-read' interface to the `kill-ring'. Additionally the Consult -version supports preview of the selected string." - (interactive "*p") - (if (eq last-command 'yank) - (yank-pop (or arg 1)) - (call-interactively #'consult-yank-from-kill-ring))) - -;; Adapted from the Emacs yank-pop function. -;;;###autoload -(defun consult-yank-replace (string) - "Select STRING from the kill ring. - -If there was no recent yank, insert the string. -Otherwise replace the just-yanked string with the selected string. - -There exists no equivalent of this command in Emacs 28." - (interactive (list (consult--read-from-kill-ring))) - (when string - (if (not (eq last-command 'yank)) - (consult-yank-from-kill-ring string) - (let ((inhibit-read-only t) - (pt (point)) - (mk (mark t))) - (setq this-command 'yank) - (funcall (or yank-undo-function 'delete-region) (min pt mk) (max pt mk)) - (setq yank-undo-function nil) - (set-marker (mark-marker) pt (current-buffer)) - (insert-for-yank string) - (set-window-start (selected-window) yank-window-start t) - (if (< pt mk) - (goto-char (prog1 (mark t) - (set-marker (mark-marker) (point) (current-buffer))))))))) - -;;;;; Command: consult-bookmark - -(defun consult--bookmark-preview () - "Create preview function for bookmarks." - (let ((preview (consult--jump-preview)) - (open (consult--temporary-files))) - (lambda (action cand) - (unless cand - (funcall open)) - (funcall - preview action - ;; Only preview bookmarks with the default handler. - (when-let ((bm (and cand (eq action 'preview) (assoc cand bookmark-alist))) - (handler (or (bookmark-get-handler bm) #'bookmark-default-handler)) - ((eq handler #'bookmark-default-handler)) - (file (bookmark-get-filename bm)) - (pos (bookmark-get-position bm)) - (buf (funcall open file))) - (set-marker (make-marker) pos buf)))))) - -(defun consult--bookmark-action (bm) - "Open BM via `consult--buffer-action'." - (bookmark-jump bm consult--buffer-display)) - -(consult--define-state bookmark) - -(defun consult--bookmark-candidates () - "Return bookmark candidates." - (bookmark-maybe-load-default-file) - (let ((narrow (cl-loop for (y _ . xs) in consult-bookmark-narrow nconc - (cl-loop for x in xs collect (cons x y))))) - (cl-loop for bm in bookmark-alist collect - (propertize (car bm) - 'consult--type - (alist-get - (or (bookmark-get-handler bm) #'bookmark-default-handler) - narrow))))) - -;;;###autoload -(defun consult-bookmark (name) - "If bookmark NAME exists, open it, otherwise create a new bookmark with NAME. - -The command supports preview of file bookmarks and narrowing. See the -variable `consult-bookmark-narrow' for the narrowing configuration." - (interactive - (list - (let ((narrow (cl-loop for (x y . _) in consult-bookmark-narrow collect (cons x y)))) - (consult--read - (consult--bookmark-candidates) - :prompt "Bookmark: " - :state (consult--bookmark-preview) - :category 'bookmark - :history 'bookmark-history - ;; Add default names to future history. - ;; Ignore errors such that `consult-bookmark' can be used in - ;; buffers which are not backed by a file. - :add-history (ignore-errors (bookmark-prop-get (bookmark-make-record) 'defaults)) - :group (consult--type-group narrow) - :narrow (consult--type-narrow narrow))))) - (bookmark-maybe-load-default-file) - (if (assoc name bookmark-alist) - (bookmark-jump name) - (bookmark-set name))) - -;;;;; Command: consult-complex-command - -;;;###autoload -(defun consult-complex-command () - "Select and evaluate command from the command history. - -This command can act as a drop-in replacement for `repeat-complex-command'." - (interactive) - (let* ((history (or (delete-dups (mapcar #'prin1-to-string command-history)) - (user-error "There are no previous complex commands"))) - (cmd (read (consult--read - history - :prompt "Command: " - :default (car history) - :sort nil - :history t ;; disable history - :category 'expression)))) - ;; Taken from `repeat-complex-command' - (add-to-history 'command-history cmd) - (apply #'funcall-interactively - (car cmd) - (mapcar (lambda (e) (eval e t)) (cdr cmd))))) - -;;;;; Command: consult-history - -(declare-function ring-elements "ring") - -(defun consult--current-history () - "Return the history and index variable relevant to the current buffer. -If the minibuffer is active, the minibuffer history is returned, -otherwise the history corresponding to the mode. There is a -special case for `repeat-complex-command', for which the command -history is used." - (cond - ;; In the minibuffer we use the current minibuffer history, - ;; which can be configured by setting `minibuffer-history-variable'. - ((minibufferp) - (when (eq minibuffer-history-variable t) - (user-error "Minibuffer history is disabled for `%s'" this-command)) - (list (mapcar #'consult--tofu-hide - (if (eq minibuffer-history-variable 'command-history) - ;; If pressing "C-x M-:", i.e., `repeat-complex-command', - ;; we are instead querying the `command-history' and get a - ;; full s-expression. Alternatively you might want to use - ;; `consult-complex-command', which can also be bound to - ;; "C-x M-:"! - (mapcar #'prin1-to-string command-history) - (symbol-value minibuffer-history-variable))))) - ;; Otherwise we use a mode-specific history, see `consult-mode-histories'. - (t (let ((found (seq-find (lambda (h) - (and (derived-mode-p (car h)) - (boundp (if (consp (cdr h)) (cadr h) (cdr h))))) - consult-mode-histories))) - (unless found - (user-error "No history configured for `%s', see `consult-mode-histories'" - major-mode)) - (cons (symbol-value (cadr found)) (cddr found)))))) - -;;;###autoload -(defun consult-history (&optional history index bol) - "Insert string from HISTORY of current buffer. -In order to select from a specific HISTORY, pass the history -variable as argument. INDEX is the name of the index variable to -update, if any. BOL is the function which jumps to the beginning -of the prompt. See also `cape-history' from the Cape package." - (interactive) - (pcase-let* ((`(,history ,index ,bol) (if history - (list history index bol) - (consult--current-history))) - (history (if (ring-p history) (ring-elements history) history)) - (`(,beg . ,end) - (if (minibufferp) - (cons (minibuffer-prompt-end) (point-max)) - (if bol - (save-excursion - (funcall bol) - (cons (point) (pos-eol))) - (cons (point) (point))))) - (str (consult--local-let ((enable-recursive-minibuffers t)) - (consult--read - (or (consult--remove-dups history) - (user-error "History is empty")) - :prompt "History: " - :history t ;; disable history - :category ;; Report category depending on history variable - (and (minibufferp) - (pcase minibuffer-history-variable - ('extended-command-history 'command) - ('buffer-name-history 'buffer) - ('face-name-history 'face) - ('read-envvar-name-history 'environment-variable) - ('bookmark-history 'bookmark) - ('file-name-history 'file))) - :sort nil - :initial (buffer-substring-no-properties beg end) - :state (consult--insertion-preview beg end))))) - (delete-region beg end) - (when index - (set index (seq-position history str))) - (insert (substring-no-properties str)))) - -;;;;; Command: consult-isearch-history - -(defun consult-isearch-forward (&optional reverse) - "Continue Isearch forward optionally in REVERSE." - (interactive) - (consult--require-minibuffer) - (setq isearch-new-forward (not reverse) isearch-new-nonincremental nil) - (funcall (or (command-remapping #'exit-minibuffer) #'exit-minibuffer))) - -(defun consult-isearch-backward (&optional reverse) - "Continue Isearch backward optionally in REVERSE." - (interactive) - (consult-isearch-forward (not reverse))) - -;; Emacs 28: hide in M-X -(put #'consult-isearch-backward 'completion-predicate #'ignore) -(put #'consult-isearch-forward 'completion-predicate #'ignore) - -(defvar-keymap consult-isearch-history-map - :doc "Additional keymap used by `consult-isearch-history'." - "<remap> <isearch-forward>" #'consult-isearch-forward - "<remap> <isearch-backward>" #'consult-isearch-backward) - -(defun consult--isearch-history-candidates () - "Return Isearch history candidates." - ;; Do not throw an error on empty history, in order to allow starting a - ;; search. We do not :require-match here. - (let ((history (if (eq t search-default-mode) - (append regexp-search-ring search-ring) - (append search-ring regexp-search-ring)))) - (delete-dups - (mapcar - (lambda (cand) - ;; The search type can be distinguished via text properties. - (let* ((props (plist-member (text-properties-at 0 cand) - 'isearch-regexp-function)) - (type (pcase (cadr props) - ((and 'nil (guard (not props))) ?r) - ('nil ?l) - ('word-search-regexp ?w) - ('isearch-symbol-regexp ?s) - ('char-fold-to-regexp ?c) - (_ ?u)))) - ;; Disambiguate history items. The same string could - ;; occur with different search types. - (consult--tofu-append cand type))) - history)))) - -(defconst consult--isearch-history-narrow - '((?c . "Char") - (?u . "Custom") - (?l . "Literal") - (?r . "Regexp") - (?s . "Symbol") - (?w . "Word"))) - -;;;###autoload -(defun consult-isearch-history () - "Read a search string with completion from the Isearch history. - -This replaces the current search string if Isearch is active, and -starts a new Isearch session otherwise." - (interactive) - (consult--forbid-minibuffer) - (let* ((isearch-message-function #'ignore) - (cursor-in-echo-area t) ;; Avoid cursor flickering - (candidates (consult--isearch-history-candidates))) - (unless isearch-mode (isearch-mode t)) - (with-isearch-suspended - (setq isearch-new-string - (consult--read - candidates - :prompt "I-search: " - :category 'consult-isearch-history - :history t ;; disable history - :sort nil - :initial isearch-string - :keymap consult-isearch-history-map - :annotate - (lambda (cand) - (consult--annotate-align - cand - (alist-get (consult--tofu-get cand) consult--isearch-history-narrow))) - :group - (lambda (cand transform) - (if transform - cand - (alist-get (consult--tofu-get cand) consult--isearch-history-narrow))) - :lookup - (lambda (selected candidates &rest _) - (if-let (found (member selected candidates)) - (substring (car found) 0 -1) - selected)) - :state - (lambda (action cand) - (when (and (eq action 'preview) cand) - (setq isearch-string cand) - (isearch-update-from-string-properties cand) - (isearch-update))) - :narrow - (list :predicate - (lambda (cand) (= (consult--tofu-get cand) consult--narrow)) - :keys consult--isearch-history-narrow)) - isearch-new-message - (mapconcat 'isearch-text-char-description isearch-new-string ""))) - ;; Setting `isearch-regexp' etc only works outside of `with-isearch-suspended'. - (unless (plist-member (text-properties-at 0 isearch-string) 'isearch-regexp-function) - (setq isearch-regexp t - isearch-regexp-function nil)))) - -;;;;; Command: consult-minor-mode-menu - -(defun consult--minor-mode-candidates () - "Return list of minor-mode candidate strings." - (mapcar - (pcase-lambda (`(,name . ,sym)) - (propertize - name - 'consult--candidate sym - 'consult--minor-mode-narrow - (logior - (ash (if (local-variable-if-set-p sym) ?l ?g) 8) - (if (and (boundp sym) (symbol-value sym)) ?i ?o)) - 'consult--minor-mode-group - (concat - (if (local-variable-if-set-p sym) "Local " "Global ") - (if (and (boundp sym) (symbol-value sym)) "On" "Off")))) - (nconc - ;; according to describe-minor-mode-completion-table-for-symbol - ;; the minor-mode-list contains *all* minor modes - (mapcar (lambda (sym) (cons (symbol-name sym) sym)) minor-mode-list) - ;; take the lighters from minor-mode-alist - (delq nil - (mapcar (pcase-lambda (`(,sym ,lighter)) - (when (and lighter (not (equal "" lighter))) - (let (message-log-max) - (setq lighter (string-trim (format-mode-line lighter))) - (unless (string-blank-p lighter) - (cons lighter sym))))) - minor-mode-alist))))) - -(defconst consult--minor-mode-menu-narrow - '((?l . "Local") - (?g . "Global") - (?i . "On") - (?o . "Off"))) - -;;;###autoload -(defun consult-minor-mode-menu () - "Enable or disable minor mode. - -This is an alternative to `minor-mode-menu-from-indicator'." - (interactive) - (call-interactively - (consult--read - (consult--minor-mode-candidates) - :prompt "Minor mode: " - :require-match t - :category 'minor-mode - :group - (lambda (cand transform) - (if transform cand (get-text-property 0 'consult--minor-mode-group cand))) - :narrow - (list :predicate - (lambda (cand) - (let ((narrow (get-text-property 0 'consult--minor-mode-narrow cand))) - (or (= (logand narrow 255) consult--narrow) - (= (ash narrow -8) consult--narrow)))) - :keys - consult--minor-mode-menu-narrow) - :lookup #'consult--lookup-candidate - :history 'consult--minor-mode-menu-history))) - -;;;;; Command: consult-theme - -;;;###autoload -(defun consult-theme (theme) - "Disable current themes and enable THEME from `consult-themes'. - -The command supports previewing the currently selected theme." - (interactive - (list - (let* ((regexp (consult--regexp-filter - (mapcar (lambda (x) (if (stringp x) x (format "\\`%s\\'" x))) - consult-themes))) - (avail-themes (seq-filter - (lambda (x) (string-match-p regexp (symbol-name x))) - (cons 'default (custom-available-themes)))) - (saved-theme (car custom-enabled-themes))) - (consult--read - (mapcar #'symbol-name avail-themes) - :prompt "Theme: " - :require-match t - :category 'theme - :history 'consult--theme-history - :lookup (lambda (selected &rest _) - (setq selected (and selected (intern-soft selected))) - (or (and selected (car (memq selected avail-themes))) - saved-theme)) - :state (lambda (action theme) - (pcase action - ('return (consult-theme (or theme saved-theme))) - ((and 'preview (guard theme)) (consult-theme theme)))) - :default (symbol-name (or saved-theme 'default)))))) - (when (eq theme 'default) (setq theme nil)) - (unless (eq theme (car custom-enabled-themes)) - (mapc #'disable-theme custom-enabled-themes) - (when theme - (if (custom-theme-p theme) - (enable-theme theme) - (load-theme theme :no-confirm))))) - -;;;;; Command: consult-buffer - -(defun consult--buffer-sort-alpha (buffers) - "Sort BUFFERS alphabetically, put starred buffers at the end." - (sort buffers - (lambda (x y) - (setq x (buffer-name x) y (buffer-name y)) - (let ((a (and (length> x 0) (eq (aref x 0) ?*))) - (b (and (length> y 0) (eq (aref y 0) ?*)))) - (if (eq a b) - (string< x y) - (not a)))))) - -(defun consult--buffer-sort-alpha-current (buffers) - "Sort BUFFERS alphabetically, put current at the beginning." - (let ((buffers (consult--buffer-sort-alpha buffers)) - (current (current-buffer))) - (if (memq current buffers) - (cons current (delq current buffers)) - buffers))) - -(defun consult--buffer-sort-visibility (buffers) - "Sort BUFFERS by visibility." - (let ((hidden) - (current (car (memq (current-buffer) buffers)))) - (consult--keep! buffers - (unless (eq it current) - (if (get-buffer-window it 'visible) - it - (push it hidden) - nil))) - (nconc (nreverse hidden) buffers (and current (list current))))) - -(defun consult--normalize-directory (dir) - "Normalize directory DIR. -DIR can be project, nil or a path." - (cond - ((eq dir 'project) (consult--project-root)) - (dir (expand-file-name dir)))) - -(defun consult--buffer-query-prompt (prompt query) - "Return a list of buffers and create an appropriate prompt string. -Return a pair of a prompt string and a list of buffers. PROMPT -is the prefix of the prompt string. QUERY specifies the buffers -to search and is passed to `consult--buffer-query'." - (let* ((dir (plist-get query :directory)) - (ndir (consult--normalize-directory dir)) - (buffers (apply #'consult--buffer-query :directory ndir query)) - (count (length buffers))) - (cons (format "%s (%d buffer%s%s): " prompt count - (if (= count 1) "" "s") - (cond - ((and ndir (eq dir 'project)) - (format ", Project %s" (consult--project-name ndir))) - (ndir (concat ", " (consult--left-truncate-file ndir))) - (t ""))) - buffers))) - -(cl-defun consult--buffer-query (&key sort directory mode as predicate (filter t) - include (exclude consult-buffer-filter) - (buffer-list t)) - "Query for a list of matching buffers. -The function supports filtering by various criteria which are -used throughout Consult. In particular it is the backbone of -most `consult-buffer-sources'. -DIRECTORY can either be the symbol project or a file name. -SORT can be visibility, alpha or nil. -FILTER can be either t, nil or invert. -EXCLUDE is a list of regexps. -INCLUDE is a list of regexps. -MODE can be a mode or a list of modes to restrict the returned buffers. -PREDICATE is a predicate function. -BUFFER-LIST is the unfiltered list of buffers. -AS is a conversion function." - (let ((root (consult--normalize-directory directory))) - (setq buffer-list (if (eq buffer-list t) (buffer-list) (copy-sequence buffer-list))) - (when sort - (setq buffer-list (funcall (intern (format "consult--buffer-sort-%s" sort)) buffer-list))) - (when (or filter mode as root) - (let ((exclude-re (consult--regexp-filter exclude)) - (include-re (consult--regexp-filter include)) - (case-fold-search)) - (consult--keep! buffer-list - (and - (or (not mode) - (let ((mm (buffer-local-value 'major-mode it))) - (if (consp mode) - (seq-some (lambda (m) (provided-mode-derived-p mm m)) mode) - (provided-mode-derived-p mm mode)))) - (pcase-exhaustive filter - ('nil t) - ((or 't 'invert) - (eq (eq filter t) - (and - (or (not exclude) - (not (string-match-p exclude-re (buffer-name it)))) - (or (not include) - (not (not (string-match-p include-re (buffer-name it))))))))) - (or (not root) - (when-let (dir (buffer-local-value 'default-directory it)) - (string-prefix-p root - (if (and (/= 0 (length dir)) (eq (aref dir 0) ?/)) - dir - (expand-file-name dir))))) - (or (not predicate) (funcall predicate it)) - (if as (funcall as it) it))))) - buffer-list)) - -(defun consult--buffer-file-hash () - "Return hash table of all buffer file names." - (consult--string-hash (consult--buffer-query :as #'buffer-file-name))) - -(defun consult--buffer-pair (buffer) - "Return a pair of name of BUFFER and BUFFER." - (cons (buffer-name buffer) buffer)) - -(defun consult--buffer-preview () - "Buffer preview function." - (let ((orig-buf (window-buffer (consult--original-window))) - (orig-prev (copy-sequence (window-prev-buffers))) - (orig-next (copy-sequence (window-next-buffers))) - other-win) - (lambda (action cand) - (pcase action - ('exit - (set-window-prev-buffers other-win orig-prev) - (set-window-next-buffers other-win orig-next)) - ('preview - (when (and (eq consult--buffer-display #'switch-to-buffer-other-window) - (not other-win)) - (switch-to-buffer-other-window orig-buf 'norecord) - (setq other-win (selected-window))) - (let ((win (or other-win (selected-window))) - (buf (or (and cand (get-buffer cand)) orig-buf))) - (when (and (window-live-p win) (buffer-live-p buf) - (not (buffer-match-p consult-preview-excluded-buffers buf))) - (with-selected-window win - (unless (or orig-prev orig-next) - (setq orig-prev (copy-sequence (window-prev-buffers)) - orig-next (copy-sequence (window-next-buffers)))) - (switch-to-buffer buf 'norecord))))))))) - -(defun consult--buffer-action (buffer &optional norecord) - "Switch to BUFFER via `consult--buffer-display' function. -If NORECORD is non-nil, do not record the buffer switch in the buffer list." - (funcall consult--buffer-display buffer norecord)) - -(consult--define-state buffer) - -(defvar consult--source-bookmark - `(:name "Bookmark" - :narrow ?m - :category bookmark - :face consult-bookmark - :history bookmark-history - :items ,#'bookmark-all-names - :state ,#'consult--bookmark-state) - "Bookmark candidate source for `consult-buffer'.") - -(defvar consult--source-project-buffer - `(:name "Project Buffer" - :narrow ?b - :category buffer - :face consult-buffer - :history buffer-name-history - :state ,#'consult--buffer-state - :enabled ,(lambda () consult-project-function) - :items - ,(lambda () - (when-let (root (consult--project-root)) - (consult--buffer-query :sort 'visibility - :directory root - :as #'consult--buffer-pair)))) - "Project buffer candidate source for `consult-buffer'.") - -(defvar consult--source-project-recent-file - `(:name "Project File" - :narrow ?f - :category file - :face consult-file - :history file-name-history - :state ,#'consult--file-state - :new - ,(lambda (file) - (consult--file-action - (expand-file-name file (consult--project-root)))) - :enabled - ,(lambda () - (and consult-project-function - recentf-mode)) - :items - ,(lambda () - (when-let (root (consult--project-root)) - (let ((len (length root)) - (ht (consult--buffer-file-hash)) - items) - (dolist (file (bound-and-true-p recentf-list) (nreverse items)) - ;; Emacs 29 abbreviates file paths by default, see - ;; `recentf-filename-handlers'. I recommend to set - ;; `recentf-filename-handlers' to nil to avoid any slow down. - (unless (eq (aref file 0) ?/) - (let (file-name-handler-alist) ;; No Tramp slowdown please. - (setq file (expand-file-name file)))) - (when (and (not (gethash file ht)) (string-prefix-p root file)) - (let ((part (substring file len))) - (when (equal part "") (setq part "./")) - (put-text-property 0 1 'multi-category `(file . ,file) part) - (push part items)))))))) - "Project file candidate source for `consult-buffer'.") - -(defvar consult--source-project-buffer-hidden - `(:hidden t :narrow (?p . "Project") ,@consult--source-project-buffer) - "Like `consult--source-project-buffer' but hidden by default.") - -(defvar consult--source-project-recent-file-hidden - `(:hidden t :narrow (?p . "Project") ,@consult--source-project-recent-file) - "Like `consult--source-project-recent-file' but hidden by default.") - -(defvar consult--source-hidden-buffer - `(:name "Hidden Buffer" - :narrow ?\s - :hidden t - :category buffer - :face consult-buffer - :history buffer-name-history - :action ,#'consult--buffer-action - :items - ,(lambda () (consult--buffer-query :sort 'visibility - :filter 'invert - :as #'consult--buffer-pair))) - "Hidden buffer candidate source for `consult-buffer'.") - -(defvar consult--source-modified-buffer - `(:name "Modified Buffer" - :narrow ?* - :hidden t - :category buffer - :face consult-buffer - :history buffer-name-history - :state ,#'consult--buffer-state - :items - ,(lambda () (consult--buffer-query :sort 'visibility - :as #'consult--buffer-pair - :predicate - (lambda (buf) - (and (buffer-modified-p buf) - (buffer-file-name buf)))))) - "Modified buffer candidate source for `consult-buffer'.") - -(defvar consult--source-buffer - `(:name "Buffer" - :narrow ?b - :category buffer - :face consult-buffer - :history buffer-name-history - :state ,#'consult--buffer-state - :default t - :items - ,(lambda () (consult--buffer-query :sort 'visibility - :as #'consult--buffer-pair))) - "Buffer candidate source for `consult-buffer'.") - -(defun consult--file-register-p (reg) - "Return non-nil if REG is a file register." - (memq (car-safe (cdr reg)) '(file-query file))) - -(autoload 'consult-register--candidates "consult-register") -(defvar consult--source-file-register - `(:name "File Register" - :narrow (?r . "Register") - :category file - :state ,#'consult--file-state - :enabled ,(lambda () (seq-some #'consult--file-register-p register-alist)) - :items ,(lambda () (consult-register--candidates #'consult--file-register-p))) - "File register source.") - -(defvar consult--source-recent-file - `(:name "File" - :narrow ?f - :category file - :face consult-file - :history file-name-history - :state ,#'consult--file-state - :new ,#'consult--file-action - :enabled ,(lambda () recentf-mode) - :items - ,(lambda () - (let ((ht (consult--buffer-file-hash)) - items) - (dolist (file (bound-and-true-p recentf-list) (nreverse items)) - ;; Emacs 29 abbreviates file paths by default, see - ;; `recentf-filename-handlers'. I recommend to set - ;; `recentf-filename-handlers' to nil to avoid any slow down. - (unless (eq (aref file 0) ?/) - (let (file-name-handler-alist) ;; No Tramp slowdown please. - (setq file (expand-file-name file)))) - (unless (gethash file ht) - (push (consult--fast-abbreviate-file-name file) items)))))) - "Recent file candidate source for `consult-buffer'.") - -;;;###autoload -(defun consult-buffer (&optional sources) - "Enhanced `switch-to-buffer' command with support for virtual buffers. - -The command supports recent files, bookmarks, views and project files as -virtual buffers. Buffers are previewed. Narrowing to buffers (b), files (f), -bookmarks (m) and project files (p) is supported via the corresponding -keys. In order to determine the project-specific files and buffers, the -`consult-project-function' is used. The virtual buffer SOURCES -default to `consult-buffer-sources'. See `consult--multi' for the -configuration of the virtual buffer sources." - (interactive) - (let ((selected (consult--multi (or sources consult-buffer-sources) - :require-match - (confirm-nonexistent-file-or-buffer) - :prompt "Switch to: " - :history 'consult--buffer-history - :sort nil))) - ;; For non-matching candidates, fall back to buffer creation. - (unless (plist-get (cdr selected) :match) - (consult--buffer-action (car selected))))) - -(defmacro consult--with-project (&rest body) - "Ensure that BODY is executed with a project root." - ;; We have to work quite hard here to ensure that the project root is - ;; only overridden at the current recursion level. When entering a - ;; recursive minibuffer session, we should be able to still switch the - ;; project. But who does that? Working on the first level on project A - ;; and on the second level on project B and on the third level on project C? - ;; You mustn't be afraid to dream a little bigger, darling. - `(let ((consult-project-function - (let ((root (or (consult--project-root t) (user-error "No project found"))) - (depth (recursion-depth)) - (orig consult-project-function)) - (lambda (may-prompt) - (if (= depth (recursion-depth)) - root - (funcall orig may-prompt)))))) - ,@body)) - -;;;###autoload -(defun consult-project-buffer () - "Enhanced `project-switch-to-buffer' command with support for virtual buffers. -The command may prompt you for a project directory if it is invoked from -outside a project. See `consult-buffer' for more details." - (interactive) - (consult--with-project - (consult-buffer consult-project-buffer-sources))) - -;;;###autoload -(defun consult-buffer-other-window () - "Variant of `consult-buffer', switching to a buffer in another window." - (interactive) - (let ((consult--buffer-display #'switch-to-buffer-other-window)) - (consult-buffer))) - -;;;###autoload -(defun consult-buffer-other-frame () - "Variant of `consult-buffer', switching to a buffer in another frame." - (interactive) - (let ((consult--buffer-display #'switch-to-buffer-other-frame)) - (consult-buffer))) - -;;;###autoload -(defun consult-buffer-other-tab () - "Variant of `consult-buffer', switching to a buffer in another tab." - (interactive) - (let ((consult--buffer-display #'switch-to-buffer-other-tab)) - (consult-buffer))) - -;;;;; Command: consult-grep - -(defun consult--grep-format (async builder) - "Return ASYNC function highlighting grep match results. -BUILDER is the command line builder function." - (let (highlight) - (lambda (action) - (cond - ((stringp action) - (setq highlight (cdr (funcall builder action))) - (funcall async action)) - ((consp action) - (let ((file "") (file-len 0) result) - (save-match-data - (dolist (str action) - (when (and (string-match consult--grep-match-regexp str) - ;; Filter out empty context lines - (or (/= (aref str (match-beginning 3)) ?-) - (/= (match-end 0) (length str)))) - ;; We share the file name across candidates to reduce - ;; the amount of allocated memory. - (unless (and (= file-len (- (match-end 1) (match-beginning 1))) - (eq t (compare-strings - file 0 file-len - str (match-beginning 1) (match-end 1) nil))) - (setq file (match-string 1 str) - file-len (length file))) - (let* ((line (match-string 2 str)) - (ctx (= (aref str (match-beginning 3)) ?-)) - (sep (if ctx "-" ":")) - (content (substring str (match-end 0))) - (line-len (length line))) - (when (length> content consult-grep-max-columns) - (setq content (substring content 0 consult-grep-max-columns))) - (when highlight - (funcall highlight content)) - (setq str (concat file sep line sep content)) - ;; Store file name in order to avoid allocations in `consult--prefix-group' - (add-text-properties 0 file-len `(face consult-file consult--prefix-group ,file) str) - (put-text-property (1+ file-len) (+ 1 file-len line-len) 'face 'consult-line-number str) - (when ctx - (add-face-text-property (+ 2 file-len line-len) (length str) 'consult-grep-context 'append str)) - (push str result))))) - (funcall async (nreverse result)))) - (t (funcall async action)))))) - -(defun consult--grep-position (cand &optional find-file) - "Return the grep position marker for CAND. -FIND-FILE is the file open function, defaulting to `find-file-noselect'." - (when cand - (let* ((file-end (next-single-property-change 0 'face cand)) - (line-end (next-single-property-change (1+ file-end) 'face cand)) - (matches (consult--point-placement cand (1+ line-end) 'consult-grep-context)) - (file (substring-no-properties cand 0 file-end)) - (line (string-to-number (substring-no-properties cand (+ 1 file-end) line-end)))) - (when-let (pos (consult--marker-from-line-column - (funcall (or find-file #'consult--file-action) file) - line (or (car matches) 0))) - (cons pos (cdr matches)))))) - -(defun consult--grep-state () - "Grep state function." - (let ((open (consult--temporary-files)) - (jump (consult--jump-state))) - (lambda (action cand) - (unless cand - (funcall open)) - (funcall jump action (consult--grep-position - cand - (and (not (eq action 'return)) open)))))) - -(defun consult--grep-exclude-args () - "Produce grep exclude arguments. -Take the variables `grep-find-ignored-directories' and -`grep-find-ignored-files' into account." - (unless (boundp 'grep-find-ignored-files) (require 'grep)) - (nconc (mapcar (lambda (s) (concat "--exclude=" s)) - (bound-and-true-p grep-find-ignored-files)) - (mapcar (lambda (s) (concat "--exclude-dir=" s)) - (bound-and-true-p grep-find-ignored-directories)))) - -(defun consult--grep (prompt make-builder dir initial) - "Run asynchronous grep. - -MAKE-BUILDER is the function that returns the command line -builder function. DIR is a directory or a list of file or -directories. PROMPT is the prompt string. INITIAL is initial -input." - (pcase-let* ((`(,prompt ,paths ,dir) (consult--directory-prompt prompt dir)) - (default-directory dir) - (builder (funcall make-builder paths))) - (consult--read - (consult--async-command builder - (consult--grep-format builder) - :file-handler t) ;; allow tramp - :prompt prompt - :lookup #'consult--lookup-member - :state (consult--grep-state) - :initial (consult--async-split-initial initial) - :add-history (consult--async-split-thingatpt 'symbol) - :require-match t - :category 'consult-grep - :group #'consult--prefix-group - :history '(:input consult--grep-history) - :sort nil))) - -(defun consult--grep-lookahead-p (&rest cmd) - "Return t if grep CMD supports look-ahead." - (eq 0 (process-file-shell-command - (concat "echo xaxbx | " - (mapconcat #'shell-quote-argument `(,@cmd "^(?=.*b)(?=.*a)") " "))))) - -(defun consult--grep-make-builder (paths) - "Build grep command line and grep across PATHS." - (let* ((cmd (consult--build-args consult-grep-args)) - (type (if (consult--grep-lookahead-p (car cmd) "-P") 'pcre 'extended))) - (lambda (input) - (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) - (flags (append cmd opts)) - (ignore-case (or (member "-i" flags) (member "--ignore-case" flags)))) - (if (or (member "-F" flags) (member "--fixed-strings" flags)) - (cons (append cmd (list "-e" arg) opts paths) - (apply-partially #'consult--highlight-regexps - (list (regexp-quote arg)) ignore-case)) - (pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg type ignore-case))) - (when re - (cons (append cmd - (list (if (eq type 'pcre) "-P" "-E") ;; perl or extended - "-e" (consult--join-regexps re type)) - opts paths) - hl)))))))) - -;;;###autoload -(defun consult-grep (&optional dir initial) - "Search with `grep' for files in DIR where the content matches a regexp. - -The initial input is given by the INITIAL argument. DIR can be -nil, a directory string or a list of file/directory paths. If -`consult-grep' is called interactively with a prefix argument, -the user can specify the directories or files to search in. -Multiple directories must be separated by comma in the -minibuffer, since they are read via `completing-read-multiple'. -By default the project directory is used if -`consult-project-function' is defined and returns non-nil. -Otherwise the `default-directory' is searched. - -The input string is split, the first part of the string (grep -input) is passed to the asynchronous grep process and the second -part of the string is passed to the completion-style filtering. - -The input string is split at a punctuation character, which is -given as the first character of the input string. The format is -similar to Perl-style regular expressions, e.g., /regexp/. -Furthermore command line options can be passed to grep, specified -behind --. The overall prompt input has the form -`#async-input -- grep-opts#filter-string'. - -Note that the grep input string is transformed from Emacs regular -expressions to Posix regular expressions. Always enter Emacs -regular expressions at the prompt. `consult-grep' behaves like -builtin Emacs search commands, e.g., Isearch, which take Emacs -regular expressions. Furthermore the asynchronous input split -into words, each word must match separately and in any order. -See `consult--regexp-compiler' for the inner workings. In order -to disable transformations of the grep input, adjust -`consult--regexp-compiler' accordingly. - -Here we give a few example inputs: - -#alpha beta : Search for alpha and beta in any order. -#alpha.*beta : Search for alpha before beta. -#\\(alpha\\|beta\\) : Search for alpha or beta (Note Emacs syntax!) -#word -- -C3 : Search for word, include 3 lines as context -#first#second : Search for first, quick filter for second. - -The symbol at point is added to the future history." - (interactive "P") - (consult--grep "Grep" #'consult--grep-make-builder dir initial)) - -;;;;; Command: consult-git-grep - -(defun consult--git-grep-make-builder (paths) - "Create grep command line builder given PATHS." - (let ((cmd (consult--build-args consult-git-grep-args))) - (lambda (input) - (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) - (flags (append cmd opts)) - (ignore-case (or (member "-i" flags) (member "--ignore-case" flags)))) - (if (or (member "-F" flags) (member "--fixed-strings" flags)) - (cons (append cmd (list "-e" arg) opts paths) - (apply-partially #'consult--highlight-regexps - (list (regexp-quote arg)) ignore-case)) - (pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg 'extended ignore-case))) - (when re - (cons (append cmd - (cdr (mapcan (lambda (x) (list "--and" "-e" x)) re)) - opts paths) - hl)))))))) - -;;;###autoload -(defun consult-git-grep (&optional dir initial) - "Search with `git grep' for files in DIR with INITIAL input. -See `consult-grep' for details." - (interactive "P") - (consult--grep "Git-grep" #'consult--git-grep-make-builder dir initial)) - -;;;;; Command: consult-ripgrep - -(defun consult--ripgrep-make-builder (paths) - "Create ripgrep command line builder given PATHS." - (let* ((cmd (consult--build-args consult-ripgrep-args)) - (type (if (consult--grep-lookahead-p (car cmd) "-P") 'pcre 'extended))) - (lambda (input) - (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) - (flags (append cmd opts)) - (ignore-case - (and (not (or (member "-s" flags) (member "--case-sensitive" flags))) - (or (member "-i" flags) (member "--ignore-case" flags) - (and (or (member "-S" flags) (member "--smart-case" flags)) - (let (case-fold-search) - ;; Case insensitive if there are no uppercase letters - (not (string-match-p "[[:upper:]]" arg)))))))) - (if (or (member "-F" flags) (member "--fixed-strings" flags)) - (cons (append cmd (list "-e" arg) opts paths) - (apply-partially #'consult--highlight-regexps - (list (regexp-quote arg)) ignore-case)) - (pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg type ignore-case))) - (when re - (cons (append cmd (and (eq type 'pcre) '("-P")) - (list "-e" (consult--join-regexps re type)) - opts paths) - hl)))))))) - -;;;###autoload -(defun consult-ripgrep (&optional dir initial) - "Search with `rg' for files in DIR with INITIAL input. -See `consult-grep' for details." - (interactive "P") - (consult--grep "Ripgrep" #'consult--ripgrep-make-builder dir initial)) - -;;;;; Command: consult-find - -(defun consult--find (prompt builder initial) - "Run find command in current directory. - -The function returns the selected file. -The filename at point is added to the future history. - -BUILDER is the command line builder function. -PROMPT is the prompt. -INITIAL is initial input." - (consult--read - (consult--async-command builder - (consult--async-map (lambda (x) (string-remove-prefix "./" x))) - (consult--async-highlight builder) - :file-handler t) ;; allow tramp - :prompt prompt - :sort nil - :require-match t - :initial (consult--async-split-initial initial) - :add-history (consult--async-split-thingatpt 'filename) - :category 'file - :history '(:input consult--find-history))) - -(defun consult--find-make-builder (paths) - "Build find command line, finding across PATHS." - (let* ((cmd (seq-mapcat (lambda (x) - (if (equal x ".") paths (list x))) - (consult--build-args consult-find-args))) - (type (if (eq 0 (process-file-shell-command - (concat (car cmd) " -regextype emacs -version"))) - 'emacs 'basic))) - (lambda (input) - (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) - ;; ignore-case=t since -iregex is used below - (`(,re . ,hl) (funcall consult--regexp-compiler arg type t))) - (when re - (cons (append cmd - (cdr (mapcan - (lambda (x) - `("-and" "-iregex" - ,(format ".*%s.*" - ;; Replace non-capturing groups with capturing groups. - ;; GNU find does not support non-capturing groups. - (replace-regexp-in-string - "\\\\(\\?:" "\\(" x 'fixedcase 'literal)))) - re)) - opts) - hl)))))) - -;;;###autoload -(defun consult-find (&optional dir initial) - "Search for files with `find' in DIR. -The file names must match the input regexp. INITIAL is the -initial minibuffer input. See `consult-grep' for details -regarding the asynchronous search and the arguments." - (interactive "P") - (pcase-let* ((`(,prompt ,paths ,dir) (consult--directory-prompt "Find" dir)) - (default-directory dir) - (builder (consult--find-make-builder paths))) - (find-file (consult--find prompt builder initial)))) - -;;;;; Command: consult-fd - -(defun consult--fd-make-builder (paths) - "Build find command line, finding across PATHS." - (let ((cmd (consult--build-args consult-fd-args))) - (lambda (input) - (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) - (flags (append cmd opts)) - (ignore-case - (and (not (or (member "-s" flags) (member "--case-sensitive" flags))) - (or (member "-i" flags) (member "--ignore-case" flags) - (let (case-fold-search) - ;; Case insensitive if there are no uppercase letters - (not (string-match-p "[[:upper:]]" arg))))))) - (if (or (member "-F" flags) (member "--fixed-strings" flags)) - (cons (append cmd (list arg) opts paths) - (apply-partially #'consult--highlight-regexps - (list (regexp-quote arg)) ignore-case)) - (pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg 'pcre ignore-case))) - (when re - (cons (append cmd - (mapcan (lambda (x) `("--and" ,x)) re) - opts - (mapcan (lambda (x) `("--search-path" ,x)) paths)) - hl)))))))) - -;;;###autoload -(defun consult-fd (&optional dir initial) - "Search for files with `fd' in DIR. -The file names must match the input regexp. INITIAL is the -initial minibuffer input. See `consult-grep' for details -regarding the asynchronous search and the arguments." - (interactive "P") - (pcase-let* ((`(,prompt ,paths ,dir) (consult--directory-prompt "Fd" dir)) - (default-directory dir) - (builder (consult--fd-make-builder paths))) - (find-file (consult--find prompt builder initial)))) - -;;;;; Command: consult-locate - -(defun consult--locate-builder (input) - "Build command line from INPUT." - (pcase-let ((`(,arg . ,opts) (consult--command-split input))) - (unless (string-blank-p arg) - (cons (append (consult--build-args consult-locate-args) - (consult--split-escaped arg) opts) - (cdr (consult--default-regexp-compiler input 'basic t)))))) - -;;;###autoload -(defun consult-locate (&optional initial) - "Search with `locate' for files which match input given INITIAL input. - -The input is treated literally such that locate can take advantage of -the locate database index. Regular expressions would often force a slow -linear search through the entire database. The locate process is started -asynchronously, similar to `consult-grep'. See `consult-grep' for more -details regarding the asynchronous search." - (interactive) - (find-file (consult--find "Locate: " #'consult--locate-builder initial))) - -;;;;; Command: consult-man - -(defun consult--man-builder (input) - "Build command line from INPUT." - (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) - (`(,re . ,hl) (funcall consult--regexp-compiler arg 'extended t))) - (when re - (cons (append (consult--build-args consult-man-args) - (list (consult--join-regexps re 'extended)) - opts) - hl)))) - -(defun consult--man-format (lines) - "Format man candidates from LINES." - (let ((candidates)) - (save-match-data - (dolist (str lines) - (when (string-match "\\`\\(.*?\\([^ ]+\\) *(\\([^,)]+\\)[^)]*).*?\\) +- +\\(.*\\)\\'" str) - (let* ((names (match-string 1 str)) - (name (match-string 2 str)) - (section (match-string 3 str)) - (desc (match-string 4 str)) - (cand (format "%s - %s" names desc))) - (add-text-properties 0 (length names) - (list 'face 'consult-file - 'consult-man (concat section " " name)) - cand) - (push cand candidates))))) - (nreverse candidates))) - -;;;###autoload -(defun consult-man (&optional initial) - "Search for man page given INITIAL input. - -The input string is not preprocessed and passed literally to the -underlying man commands. The man process is started asynchronously, -similar to `consult-grep'. See `consult-grep' for more details regarding -the asynchronous search." - (interactive) - (man (consult--read - (consult--async-command #'consult--man-builder - (consult--async-transform consult--man-format) - (consult--async-highlight #'consult--man-builder)) - :prompt "Manual entry: " - :require-match t - :category 'consult-man - :lookup (apply-partially #'consult--lookup-prop 'consult-man) - :initial (consult--async-split-initial initial) - :add-history (consult--async-split-thingatpt 'symbol) - :history '(:input consult--man-history)))) - -;;;; Preview at point in completions buffers - -(define-minor-mode consult-preview-at-point-mode - "Preview minor mode for *Completions* buffers. -When moving around in the *Completions* buffer, the candidate at point is -automatically previewed." - :group 'consult - (if consult-preview-at-point-mode - (add-hook 'post-command-hook #'consult-preview-at-point nil 'local) - (remove-hook 'post-command-hook #'consult-preview-at-point 'local))) - -(defun consult-preview-at-point () - "Preview candidate at point in *Completions* buffer." - (interactive) - (when-let ((win (active-minibuffer-window)) - (buf (window-buffer win)) - (fun (buffer-local-value 'consult--preview-function buf))) - (funcall fun))) - -;;;; Integration with completion systems - -;;;;; Integration: Default *Completions* - -(defun consult--default-completion-minibuffer-candidate () - "Return current minibuffer candidate from default completion system or Icomplete." - (when (and (minibufferp) - (eq completing-read-function #'completing-read-default)) - (let ((content (minibuffer-contents-no-properties))) - ;; When the current minibuffer content matches a candidate, return it! - (if (test-completion content - minibuffer-completion-table - minibuffer-completion-predicate) - content - ;; Return the full first candidate of the sorted completion list. - (when-let ((completions (completion-all-sorted-completions))) - (concat - (substring content 0 (or (cdr (last completions)) 0)) - (car completions))))))) - -(defun consult--default-completion-list-candidate () - "Return current candidate at point from completions buffer." - (let (beg end) - (when (and - (derived-mode-p 'completion-list-mode) - ;; Logic taken from `choose-completion'. - ;; TODO Upstream a `completion-list-get-candidate' function. - (cond - ((and (not (eobp)) (get-text-property (point) 'mouse-face)) - (setq end (point) beg (1+ (point)))) - ((and (not (bobp)) (get-text-property (1- (point)) 'mouse-face)) - (setq end (1- (point)) beg (point))))) - (setq beg (previous-single-property-change beg 'mouse-face) - end (or (next-single-property-change end 'mouse-face) (point-max))) - (or (get-text-property beg 'completion--string) - (buffer-substring-no-properties beg end))))) - -;;;;; Integration: Vertico - -(defvar vertico--input) -(declare-function vertico--exhibit "ext:vertico") -(declare-function vertico--candidate "ext:vertico") -(declare-function vertico--filter-completions "ext:vertico") - -(defun consult--vertico-candidate () - "Return current candidate for Consult preview." - (and vertico--input (vertico--candidate 'highlight))) - -(defun consult--vertico-refresh () - "Refresh completion UI." - (when vertico--input - (setq vertico--input t) - (vertico--exhibit))) - -(defun consult--vertico-filter-adv (orig pattern cands category highlight) - "Advice for ORIG `consult--completion-filter' function. -See `consult--completion-filter' for arguments PATTERN, CANDS, CATEGORY -and HIGHLIGHT." - (if (and (not highlight) (bound-and-true-p vertico-mode)) - ;; Optimize `consult--completion-filter' using the deferred highlighting - ;; from Vertico. The advice is not necessary - it is a pure optimization. - (nconc (car (vertico--filter-completions pattern cands nil (length pattern) - `(metadata (category . ,category)))) - nil) - (funcall orig pattern cands category highlight))) - -(with-eval-after-load 'vertico - (advice-add #'consult--completion-filter :around #'consult--vertico-filter-adv) - (add-hook 'consult--completion-candidate-hook #'consult--vertico-candidate) - (add-hook 'consult--completion-refresh-hook #'consult--vertico-refresh) - (define-key consult-async-map [remap vertico-insert] 'vertico-next-group)) - -;;;;; Integration: Mct - -(with-eval-after-load 'mct (add-hook 'consult--completion-refresh-hook - 'mct--live-completions-refresh)) - -;;;;; Integration: Icomplete - -(defvar icomplete-mode) -(declare-function icomplete-exhibit "icomplete") - -(defun consult--icomplete-refresh () - "Refresh icomplete view." - (when icomplete-mode - (let ((top (car completion-all-sorted-completions))) - (completion--flush-all-sorted-completions) - ;; force flushing, otherwise narrowing is broken! - (setq completion-all-sorted-completions nil) - (when top - (let* ((completions (completion-all-sorted-completions)) - (last (last completions)) - (before)) ;; completions before top - ;; warning: completions is an improper list - (while (consp completions) - (if (equal (car completions) top) - (progn - (setcdr last (append (nreverse before) (cdr last))) - (setq completion-all-sorted-completions completions - completions nil)) - (push (car completions) before) - (setq completions (cdr completions))))))) - (icomplete-exhibit))) - -(with-eval-after-load 'icomplete - (add-hook 'consult--completion-refresh-hook #'consult--icomplete-refresh)) - -(provide 'consult) -;;; consult.el ends here diff --git a/emacs/elpa/consult-20240725.508/consult.elc b/emacs/elpa/consult-20240725.508/consult.elc Binary files differ. diff --git a/emacs/elpa/consult-20240725.508/consult-autoloads.el b/emacs/elpa/consult-20240811.1858/consult-autoloads.el diff --git a/emacs/elpa/consult-20240725.508/consult-compile.el b/emacs/elpa/consult-20240811.1858/consult-compile.el diff --git a/emacs/elpa/consult-20240725.508/consult-compile.elc b/emacs/elpa/consult-20240811.1858/consult-compile.elc Binary files differ. diff --git a/emacs/elpa/consult-20240725.508/consult-flymake.el b/emacs/elpa/consult-20240811.1858/consult-flymake.el diff --git a/emacs/elpa/consult-20240725.508/consult-flymake.elc b/emacs/elpa/consult-20240811.1858/consult-flymake.elc Binary files differ. diff --git a/emacs/elpa/consult-20240725.508/consult-imenu.el b/emacs/elpa/consult-20240811.1858/consult-imenu.el diff --git a/emacs/elpa/consult-20240725.508/consult-imenu.elc b/emacs/elpa/consult-20240811.1858/consult-imenu.elc Binary files differ. diff --git a/emacs/elpa/consult-20240725.508/consult-info.el b/emacs/elpa/consult-20240811.1858/consult-info.el diff --git a/emacs/elpa/consult-20240725.508/consult-info.elc b/emacs/elpa/consult-20240811.1858/consult-info.elc Binary files differ. diff --git a/emacs/elpa/consult-20240725.508/consult-kmacro.el b/emacs/elpa/consult-20240811.1858/consult-kmacro.el diff --git a/emacs/elpa/consult-20240725.508/consult-kmacro.elc b/emacs/elpa/consult-20240811.1858/consult-kmacro.elc Binary files differ. diff --git a/emacs/elpa/consult-20240725.508/consult-org.el b/emacs/elpa/consult-20240811.1858/consult-org.el diff --git a/emacs/elpa/consult-20240811.1858/consult-org.elc b/emacs/elpa/consult-20240811.1858/consult-org.elc Binary files differ. diff --git a/emacs/elpa/consult-20240811.1858/consult-pkg.el b/emacs/elpa/consult-20240811.1858/consult-pkg.el @@ -0,0 +1,13 @@ +(define-package "consult" "20240811.1858" "Consulting completing-read" + '((emacs "27.1") + (compat "30")) + :commit "3d0fc6a8c6a74b21ca854b8632b3f58e0e513b85" :maintainers + '(("Daniel Mendler" . "mail@daniel-mendler.de")) + :maintainer + '("Daniel Mendler" . "mail@daniel-mendler.de") + :keywords + '("matching" "files" "completion") + :url "https://github.com/minad/consult") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs/elpa/consult-20240725.508/consult-register.el b/emacs/elpa/consult-20240811.1858/consult-register.el diff --git a/emacs/elpa/consult-20240725.508/consult-register.elc b/emacs/elpa/consult-20240811.1858/consult-register.elc Binary files differ. diff --git a/emacs/elpa/consult-20240725.508/consult-xref.el b/emacs/elpa/consult-20240811.1858/consult-xref.el diff --git a/emacs/elpa/consult-20240725.508/consult-xref.elc b/emacs/elpa/consult-20240811.1858/consult-xref.elc Binary files differ. diff --git a/emacs/elpa/consult-20240811.1858/consult.el b/emacs/elpa/consult-20240811.1858/consult.el @@ -0,0 +1,5274 @@ +;;; consult.el --- Consulting completing-read -*- lexical-binding: t -*- + +;; Copyright (C) 2021-2024 Free Software Foundation, Inc. + +;; Author: Daniel Mendler and Consult contributors +;; Maintainer: Daniel Mendler <mail@daniel-mendler.de> +;; Created: 2020 +;; Version: 1.8 +;; Package-Requires: ((emacs "27.1") (compat "30")) +;; Homepage: https://github.com/minad/consult +;; Keywords: matching, files, 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: + +;; Consult implements a set of `consult-<thing>' commands, which aim to +;; improve the way you use Emacs. The commands are founded on +;; `completing-read', which selects from a list of candidate strings. +;; Consult provides an enhanced buffer switcher `consult-buffer' and +;; search and navigation commands like `consult-imenu' and +;; `consult-line'. Searching through multiple files is supported by the +;; asynchronous `consult-grep' command. Many Consult commands support +;; previewing candidates. If a candidate is selected in the completion +;; view, the buffer shows the candidate immediately. + +;; The Consult commands are compatible with multiple completion systems +;; based on the Emacs `completing-read' API, including the default +;; completion system, Vertico, Mct and Icomplete. + +;; See the README for an overview of the available Consult commands and +;; the documentation of the configuration and installation of the +;; package. + +;; The full list of contributors can be found in the acknowledgments +;; section of the README. + +;;; Code: + +(eval-when-compile + (require 'cl-lib) + (require 'subr-x)) +(require 'compat) +(require 'bookmark) + +(defgroup consult nil + "Consulting `completing-read'." + :link '(info-link :tag "Info Manual" "(consult)") + :link '(url-link :tag "Homepage" "https://github.com/minad/consult") + :link '(emacs-library-link :tag "Library Source" "consult.el") + :group 'files + :group 'outlines + :group 'minibuffer + :prefix "consult-") + +;;;; Customization + +(defcustom consult-narrow-key nil + "Prefix key for narrowing during completion. + +Good choices for this key are \"<\" and \"C-+\" for example. The +key must be a string accepted by `key-valid-p'." + :type '(choice key (const :tag "None" nil))) + +(defcustom consult-widen-key nil + "Key used for widening during completion. + +If this key is unset, defaults to twice the `consult-narrow-key'. +The key must be a string accepted by `key-valid-p'." + :type '(choice key (const :tag "None" nil))) + +(defcustom consult-project-function + #'consult--default-project-function + "Function which returns project root directory. +The function takes one boolean argument MAY-PROMPT. If +MAY-PROMPT is non-nil, the function may ask the prompt the user +for a project directory. The root directory is used by +`consult-buffer' and `consult-grep'." + :type `(choice + (const :tag "Default project function" ,#'consult--default-project-function) + (function :tag "Custom function") + (const :tag "No project integration" nil))) + +(defcustom consult-async-refresh-delay 0.2 + "Refreshing delay of the completion UI for asynchronous commands. + +The completion UI is only updated every +`consult-async-refresh-delay' seconds. This applies to +asynchronous commands like for example `consult-grep'." + :type '(float :tag "Delay in seconds")) + +(defcustom consult-async-input-throttle 0.5 + "Input throttle for asynchronous commands. + +The asynchronous process is started only every +`consult-async-input-throttle' seconds. This applies to asynchronous +commands, e.g., `consult-grep'." + :type '(float :tag "Delay in seconds")) + +(defcustom consult-async-input-debounce 0.2 + "Input debounce for asynchronous commands. + +The asynchronous process is started only when there has not been new +input for `consult-async-input-debounce' seconds. This applies to +asynchronous commands, e.g., `consult-grep'." + :type '(float :tag "Delay in seconds")) + +(defcustom consult-async-min-input 3 + "Minimum number of characters needed, before asynchronous process is called. + +This applies to asynchronous commands, e.g., `consult-grep'." + :type '(natnum :tag "Number of characters")) + +(defcustom consult-async-split-style 'perl + "Async splitting style, see `consult-async-split-styles-alist'." + :type '(choice (const :tag "No splitting" nil) + (const :tag "Comma" comma) + (const :tag "Semicolon" semicolon) + (const :tag "Perl" perl))) + +(defcustom consult-async-split-styles-alist + `((nil :function ,#'consult--split-nil) + (comma :separator ?, :function ,#'consult--split-separator) + (semicolon :separator ?\; :function ,#'consult--split-separator) + (perl :initial "#" :function ,#'consult--split-perl)) + "Async splitting styles." + :type '(alist :key-type symbol :value-type plist)) + +(defcustom consult-mode-histories + '((eshell-mode eshell-history-ring eshell-history-index eshell-bol) + (comint-mode comint-input-ring comint-input-ring-index comint-bol) + (term-mode term-input-ring term-input-ring-index term-bol)) + "Alist of mode histories (mode history index bol). +The histories can be rings or lists. Index, if provided, is a +variable to set to the index of the selection within the ring or +list. Bol, if provided is a function which jumps to the beginning +of the line after the prompt." + :type '(alist :key-type symbol + :value-type (group :tag "Include Index" + (symbol :tag "List/Ring") + (symbol :tag "Index Variable") + (symbol :tag "Bol Function")))) + +(defcustom consult-themes nil + "List of themes (symbols or regexps) to be presented for selection. +nil shows all `custom-available-themes'." + :type '(repeat (choice symbol regexp))) + +(defcustom consult-after-jump-hook (list #'recenter) + "Function called after jumping to a location. + +Commonly used functions for this hook are `recenter' and +`reposition-window'. You may want to add a function which pulses +the current line, e.g., `pulse-momentary-highlight-one-line' is +supported on Emacs 28 and newer. The hook called during preview +and for the jump after selection." + :type 'hook) + +(defcustom consult-line-start-from-top nil + "Start search from the top if non-nil. +Otherwise start the search at the current line and wrap around." + :type 'boolean) + +(defcustom consult-point-placement 'match-beginning + "Where to leave point when jumping to a match. +This setting affects the command `consult-line' and the `consult-grep' variants." + :type '(choice (const :tag "Beginning of the line" line-beginning) + (const :tag "Beginning of the match" match-beginning) + (const :tag "End of the match" match-end))) + +(defcustom consult-line-numbers-widen t + "Show absolute line numbers when narrowing is active. + +See also `display-line-numbers-widen'." + :type 'boolean) + +(defcustom consult-goto-line-numbers t + "Show line numbers for `consult-goto-line'." + :type 'boolean) + +(defcustom consult-fontify-preserve t + "Preserve fontification for line-based commands." + :type 'boolean) + +(defcustom consult-fontify-max-size 1048576 + "Buffers larger than this byte limit are not fontified. + +This is necessary in order to prevent a large startup time +for navigation commands like `consult-line'." + :type '(natnum :tag "Buffer size in bytes")) + +(defcustom consult-buffer-filter + '("\\` " + "\\`\\*Completions\\*\\'" + "\\`\\*Flymake log\\*\\'" + "\\`\\*Semantic SymRef\\*\\'" + "\\`\\*vc\\*\\'" + "\\`newsrc-dribble\\'" ;; Gnus + "\\`\\*tramp/.*\\*\\'") + "Filter regexps for `consult-buffer'. + +The default setting is to filter ephemeral buffer names beginning +with a space character, the *Completions* buffer and a few log +buffers. The regular expressions are matched case sensitively." + :type '(repeat regexp)) + +(defcustom consult-buffer-sources + '(consult--source-hidden-buffer + consult--source-modified-buffer + consult--source-buffer + consult--source-recent-file + consult--source-file-register + consult--source-bookmark + consult--source-project-buffer-hidden + consult--source-project-recent-file-hidden) + "Sources used by `consult-buffer'. +See also `consult-project-buffer-sources'. +See `consult--multi' for a description of the source data structure." + :type '(repeat symbol)) + +(defcustom consult-project-buffer-sources + '(consult--source-project-buffer + consult--source-project-recent-file) + "Sources used by `consult-project-buffer'. +See also `consult-buffer-sources'. +See `consult--multi' for a description of the source data structure." + :type '(repeat symbol)) + +(defcustom consult-mode-command-filter + '(;; Filter commands + "-mode\\'" "--" + ;; Filter whole features + simple mwheel time so-long recentf tab-bar tab-line) + "Filter commands for `consult-mode-command'." + :type '(repeat (choice symbol regexp))) + +(defcustom consult-grep-max-columns 300 + "Maximal number of columns of grep output." + :type 'natnum) + +(defconst consult--grep-match-regexp + "\\`\\(?:\\./\\)?\\([^\n\0]+\\)\0\\([0-9]+\\)\\([-:\0]\\)" + "Regexp used to match file and line of grep output.") + +(defcustom consult-grep-args + '("grep" (consult--grep-exclude-args) + "--null --line-buffered --color=never --ignore-case\ + --with-filename --line-number -I -r") + "Command line arguments for grep, see `consult-grep'. +The dynamically computed arguments are appended. +Can be either a string, or a list of strings or expressions." + :type '(choice string (repeat (choice string sexp)))) + +(defcustom consult-git-grep-args + "git --no-pager grep --null --color=never --ignore-case\ + --extended-regexp --line-number -I" + "Command line arguments for git-grep, see `consult-git-grep'. +The dynamically computed arguments are appended. +Can be either a string, or a list of strings or expressions." + :type '(choice string (repeat (choice string sexp)))) + +(defcustom consult-ripgrep-args + "rg --null --line-buffered --color=never --max-columns=1000 --path-separator /\ + --smart-case --no-heading --with-filename --line-number --search-zip" + "Command line arguments for ripgrep, see `consult-ripgrep'. +The dynamically computed arguments are appended. +Can be either a string, or a list of strings or expressions." + :type '(choice string (repeat (choice string sexp)))) + +(defcustom consult-find-args + "find . -not ( -path */.[A-Za-z]* -prune )" + "Command line arguments for find, see `consult-find'. +The dynamically computed arguments are appended. +Can be either a string, or a list of strings or expressions." + :type '(choice string (repeat (choice string sexp)))) + +(defcustom consult-fd-args + '((if (executable-find "fdfind" 'remote) "fdfind" "fd") + "--full-path --color=never") + "Command line arguments for fd, see `consult-fd'. +The dynamically computed arguments are appended. +Can be either a string, or a list of strings or expressions." + :type '(choice string (repeat (choice string sexp)))) + +(defcustom consult-locate-args + "locate --ignore-case" ;; --existing not supported by Debian plocate + "Command line arguments for locate, see `consult-locate'. +The dynamically computed arguments are appended. +Can be either a string, or a list of strings or expressions." + :type '(choice string (repeat (choice string sexp)))) + +(defcustom consult-man-args + "man -k" + "Command line arguments for man, see `consult-man'. +The dynamically computed arguments are appended. +Can be either a string, or a list of strings or expressions." + :type '(choice string (repeat (choice string sexp)))) + +(defcustom consult-preview-key 'any + "Preview trigger keys, can be nil, `any', a single key or a list of keys. +Debouncing can be specified via the `:debounce' attribute. The +individual keys must be strings accepted by `key-valid-p'." + :type '(choice (const :tag "Any key" any) + (list :tag "Debounced" + (const :debounce) + (float :tag "Seconds" 0.1) + (const any)) + (const :tag "No preview" nil) + (key :tag "Key") + (repeat :tag "List of keys" key))) + +(defcustom consult-preview-partial-size 1048576 + "Files larger than this byte limit are previewed partially." + :type '(natnum :tag "File size in bytes")) + +(defcustom consult-preview-partial-chunk 102400 + "Partial preview chunk size in bytes. +If a file is larger than `consult-preview-partial-size' only the +chunk from the beginning of the file is previewed." + :type '(natnum :tag "Chunk size in bytes")) + +(defcustom consult-preview-max-count 10 + "Number of file buffers to keep open temporarily during preview." + :type '(natnum :tag "Number of buffers")) + +(defcustom consult-preview-excluded-buffers nil + "Buffers excluded from preview. +The value should conform to the predicate format demanded by the +function `buffer-match-p'." + :type 'sexp) + +(defcustom consult-preview-excluded-files + '("\\`/[^/|:]+:") ;; Do not preview remote files + "List of regexps matched against names of files, which are not previewed." + :type '(repeat regexp)) + +(defcustom consult-preview-allowed-hooks + '(global-font-lock-mode + save-place-find-file-hook) + "List of hooks, which should be executed during file preview. +This variable applies to `find-file-hook', `change-major-mode-hook' and +mode hooks, e.g., `prog-mode-hook'." + :type '(repeat symbol)) + +(defcustom consult-preview-variables + '((inhibit-message . t) + (enable-dir-local-variables . nil) + (enable-local-variables . :safe) + (non-essential . t) + (delay-mode-hooks . t)) + "Variables which are bound for file preview." + :type '(alist :key-type symbol)) + +(defcustom consult-bookmark-narrow + `((?f "File" bookmark-default-handler) + (?h "Help" help-bookmark-jump Info-bookmark-jump + Man-bookmark-jump woman-bookmark-jump) + (?p "Picture" image-bookmark-jump) + (?d "Docview" doc-view-bookmark-jump) + (?m "Mail" gnus-summary-bookmark-jump) + (?s "Eshell" eshell-bookmark-jump) + (?w "Web" eww-bookmark-jump xwidget-webkit-bookmark-jump-handler) + (?v "VC Directory" vc-dir-bookmark-jump) + (nil "Other")) + "Bookmark narrowing configuration. + +Each element of the list must have the form (char name handlers...)." + :type '(alist :key-type character :value-type (cons string (repeat function)))) + +(defcustom consult-yank-rotate + (if (boundp 'yank-from-kill-ring-rotate) + yank-from-kill-ring-rotate + t) + "Rotate the `kill-ring' in the `consult-yank' commands." + :type 'boolean) + +;;;; Faces + +(defgroup consult-faces nil + "Faces used by Consult." + :group 'consult + :group 'faces) + +(defface consult-preview-line + '((t :inherit consult-preview-insertion :extend t)) + "Face used for line previews.") + +(defface consult-highlight-match + '((t :inherit match)) + "Face used to highlight matches in the completion candidates. +Used for example by `consult-grep'.") + +(defface consult-highlight-mark + '((t :inherit consult-highlight-match)) + "Face used for mark positions in completion candidates. +Used for example by `consult-mark'. The face should be different +than the `cursor' face to avoid confusion.") + +(defface consult-preview-match + '((t :inherit isearch)) + "Face used for match previews, e.g., in `consult-line'.") + +(defface consult-preview-insertion + '((t :inherit region)) + "Face used for previews of text to be inserted. +Used by `consult-completion-in-region', `consult-yank' and `consult-history'.") + +(defface consult-narrow-indicator + '((t :inherit warning)) + "Face used for the narrowing indicator.") + +(defface consult-async-running + '((t :inherit consult-narrow-indicator)) + "Face used if asynchronous process is running.") + +(defface consult-async-finished + '((t :inherit success)) + "Face used if asynchronous process has finished.") + +(defface consult-async-failed + '((t :inherit error)) + "Face used if asynchronous process has failed.") + +(defface consult-async-split + '((t :inherit font-lock-negation-char-face)) + "Face used to highlight punctuation character.") + +(defface consult-help + '((t :inherit shadow)) + "Face used to highlight help, e.g., in `consult-register-store'.") + +(defface consult-key + '((t :inherit font-lock-keyword-face)) + "Face used to highlight keys, e.g., in `consult-register'.") + +(defface consult-line-number + '((t :inherit consult-key)) + "Face used to highlight location line in `consult-global-mark'.") + +(defface consult-file + '((t :inherit font-lock-function-name-face)) + "Face used to highlight files in `consult-buffer'.") + +(defface consult-grep-context + '((t :inherit shadow)) + "Face used to highlight grep context in `consult-grep'.") + +(defface consult-bookmark + '((t :inherit font-lock-constant-face)) + "Face used to highlight bookmarks in `consult-buffer'.") + +(defface consult-buffer + '((t)) + "Face used to highlight buffers in `consult-buffer'.") + +(defface consult-line-number-prefix + '((t :inherit line-number)) + "Face used to highlight line number prefixes.") + +(defface consult-line-number-wrapped + '((t :inherit consult-line-number-prefix :inherit font-lock-warning-face)) + "Face used to highlight line number prefixes after wrap around.") + +(defface consult-separator + '((((class color) (min-colors 88) (background light)) + :foreground "#ccc") + (((class color) (min-colors 88) (background dark)) + :foreground "#333")) + "Face used for thin line separators in `consult-register-window'.") + +;;;; Input history variables + +(defvar consult--path-history nil) +(defvar consult--grep-history nil) +(defvar consult--find-history nil) +(defvar consult--man-history nil) +(defvar consult--line-history nil) +(defvar consult--line-multi-history nil) +(defvar consult--theme-history nil) +(defvar consult--minor-mode-menu-history nil) +(defvar consult--buffer-history nil) + +;;;; Internal variables + +(defvar consult--regexp-compiler + #'consult--default-regexp-compiler + "Regular expression compiler used by `consult-grep' and other commands. +The function must return a list of regular expressions and a highlighter +function.") + +(defvar consult--customize-alist + ;; Disable preview in frames, since `consult--jump-preview' does not properly + ;; clean up. See gh:minad/consult#593. This issue should better be fixed in + ;; `consult--jump-preview'. + `((,#'consult-buffer-other-frame :preview-key nil) + (,#'consult-buffer-other-tab :preview-key nil)) + "Command configuration alist for fine-grained configuration. + +Each element of the list must have the form (command-name plist...). The +options set here will be evaluated and passed to `consult--read', when +called from the corresponding command. Note that the options depend on +the private `consult--read' API and should not be considered as stable +as the public API.") + +(defvar consult--buffer-display #'switch-to-buffer + "Buffer display function.") + +(defvar consult--completion-candidate-hook + (list #'consult--default-completion-minibuffer-candidate + #'consult--default-completion-list-candidate) + "Get candidate from completion system.") + +(defvar consult--completion-refresh-hook nil + "Refresh completion system.") + +(defvar-local consult--preview-function nil + "Minibuffer-local variable which exposes the current preview function. +This function can be called by custom completion systems from +outside the minibuffer.") + +(defvar consult--annotate-align-step 10 + "Round candidate width.") + +(defvar consult--annotate-align-width 0 + "Maximum candidate width used for annotation alignment.") + +(defconst consult--tofu-char #x200000 + "Special character used to encode line prefixes for disambiguation. +We use invalid characters outside the Unicode range.") + +(defconst consult--tofu-range #x100000 + "Special character range.") + +(defvar-local consult--narrow nil + "Current narrowing key.") + +(defvar-local consult--narrow-keys nil + "Narrowing prefixes of the current completion.") + +(defvar-local consult--narrow-predicate nil + "Narrowing predicate of the current completion.") + +(defvar-local consult--narrow-overlay nil + "Narrowing indicator overlay.") + +(defvar consult--gc-threshold (* 64 1024 1024) + "Large GC threshold for temporary increase.") + +(defvar consult--gc-percentage 0.5 + "Large GC percentage for temporary increase.") + +(defvar consult--process-chunk (* 1024 1024) + "Increase process output chunk size.") + +(defvar consult--async-log + " *consult-async*" + "Buffer for async logging output used by `consult--async-process'.") + +(defvar-local consult--focus-lines-overlays nil + "Overlays used by `consult-focus-lines'.") + +(defvar-local consult--org-fold-regions nil + "Stored regions for the org-fold API.") + +;;;; Miscellaneous helper functions + +(defun consult--key-parse (key) + "Parse KEY or signal error if invalid." + (unless (key-valid-p key) + (error "%S is not a valid key definition; see `key-valid-p'" key)) + (key-parse key)) + +(defun consult--in-buffer (fun &optional buffer) + "Ensure that FUN is executed inside BUFFER." + (unless buffer (setq buffer (current-buffer))) + (lambda (&rest args) + (with-current-buffer buffer + (apply fun args)))) + +(defun consult--completion-table-in-buffer (table &optional buffer) + "Ensure that completion TABLE is executed inside BUFFER." + (if (functionp table) + (consult--in-buffer + (lambda (str pred action) + (let ((result (funcall table str pred action))) + (pcase action + ('metadata + (setq result + (mapcar + (lambda (x) + (if (and (string-suffix-p "-function" (symbol-name (car-safe x))) (cdr x)) + (cons (car x) (consult--in-buffer (cdr x))) + x)) + result))) + ((and 'completion--unquote (guard (functionp (cadr result)))) + (cl-callf consult--in-buffer (cadr result) buffer) + (cl-callf consult--in-buffer (cadddr result) buffer))) + result)) + buffer) + table)) + +(defun consult--build-args (arg) + "Return ARG as a flat list of split strings. + +Turn ARG into a list, and for each element either: +- split it if it a string. +- eval it if it is an expression." + (seq-mapcat (lambda (x) + (if (stringp x) + (split-string-and-unquote x) + (ensure-list (eval x 'lexical)))) + (ensure-list arg))) + +(defun consult--command-split (str) + "Return command argument and options list given input STR." + (save-match-data + (let ((opts (when (string-match " +--\\( +\\|\\'\\)" str) + (prog1 (substring str (match-end 0)) + (setq str (substring str 0 (match-beginning 0))))))) + ;; split-string-and-unquote fails if the quotes are invalid. Ignore it. + (cons str (and opts (ignore-errors (split-string-and-unquote opts))))))) + +(defmacro consult--keep! (list form) + "Evaluate FORM for every element of LIST and keep the non-nil results." + (declare (indent 1)) + (cl-with-gensyms (head prev result) + `(let* ((,head (cons nil ,list)) + (,prev ,head)) + (while (cdr ,prev) + (if-let (,result (let ((it (cadr ,prev))) ,form)) + (progn + (pop ,prev) + (setcar ,prev ,result)) + (setcdr ,prev (cddr ,prev)))) + (setq ,list (cdr ,head)) + nil))) + +;; Upstream bug#46326, Consult issue gh:minad/consult#193. +(defmacro consult--minibuffer-with-setup-hook (fun &rest body) + "Variant of `minibuffer-with-setup-hook' using a symbol and `fset'. + +This macro is only needed to prevent memory leaking issues with +the upstream `minibuffer-with-setup-hook' macro. +FUN is the hook function and BODY opens the minibuffer." + (declare (indent 1) (debug t)) + (let ((hook (gensym "hook")) + (append)) + (when (eq (car-safe fun) :append) + (setq append '(t) fun (cadr fun))) + `(let ((,hook (make-symbol "consult--minibuffer-setup-hook"))) + (fset ,hook (lambda () + (remove-hook 'minibuffer-setup-hook ,hook) + (funcall ,fun))) + (unwind-protect + (progn + (add-hook 'minibuffer-setup-hook ,hook ,@append) + ,@body) + (remove-hook 'minibuffer-setup-hook ,hook))))) + +(defun consult--completion-filter (pattern cands category _highlight) + "Filter CANDS with PATTERN. + +CATEGORY is the completion category, used to find the completion style via +`completion-category-defaults' and `completion-category-overrides'. +HIGHLIGHT must be non-nil if the resulting strings should be highlighted." + ;; completion-all-completions returns an improper list + ;; where the last link is not necessarily nil. + (nconc (completion-all-completions pattern cands nil (length pattern) + `(metadata (category . ,category))) + nil)) + +(defun consult--completion-filter-complement (pattern cands category _highlight) + "Filter CANDS with complement of PATTERN. +See `consult--completion-filter' for the arguments CATEGORY and HIGHLIGHT." + (let ((ht (consult--string-hash (consult--completion-filter pattern cands category nil)))) + (seq-remove (lambda (x) (gethash x ht)) cands))) + +(defun consult--completion-filter-dispatch (pattern cands category highlight) + "Filter CANDS with PATTERN with optional complement. +Either using `consult--completion-filter' or +`consult--completion-filter-complement', depending on if the pattern starts +with a bang. See `consult--completion-filter' for the arguments CATEGORY and +HIGHLIGHT." + (cond + ((string-match-p "\\`!? ?\\'" pattern) cands) ;; empty pattern + ((string-prefix-p "! " pattern) (consult--completion-filter-complement + (substring pattern 2) cands category nil)) + (t (consult--completion-filter pattern cands category highlight)))) + +(defmacro consult--each-line (beg end &rest body) + "Iterate over each line. + +The line beginning/ending BEG/END is bound in BODY." + (declare (indent 2)) + (cl-with-gensyms (max) + `(save-excursion + (let ((,beg (point-min)) (,max (point-max)) ,end) + (while (< ,beg ,max) + (goto-char ,beg) + (setq ,end (pos-eol)) + ,@body + (setq ,beg (1+ ,end))))))) + +(defun consult--display-width (string) + "Compute width of STRING taking display and invisible properties into account." + (let ((pos 0) (width 0) (end (length string))) + (while (< pos end) + (let ((nextd (next-single-property-change pos 'display string end)) + (display (get-text-property pos 'display string))) + (if (stringp display) + (setq width (+ width (string-width display)) + pos nextd) + (while (< pos nextd) + (let ((nexti (next-single-property-change pos 'invisible string nextd))) + (unless (get-text-property pos 'invisible string) + (setq width (+ width (compat-call string-width string pos nexti)))) + (setq pos nexti)))))) + width)) + +(defun consult--string-hash (strings) + "Create hash table from STRINGS." + (let ((ht (make-hash-table :test #'equal :size (length strings)))) + (dolist (str strings) + (puthash str t ht)) + ht)) + +(defmacro consult--local-let (binds &rest body) + "Buffer local let BINDS of dynamic variables in BODY." + (declare (indent 1)) + (let ((buffer (gensym "buffer")) + (local (mapcar (lambda (x) (cons (gensym "local") (car x))) binds))) + `(let ((,buffer (current-buffer)) + ,@(mapcar (lambda (x) `(,(car x) (local-variable-p ',(cdr x)))) local)) + (unwind-protect + (progn + ,@(mapcar (lambda (x) `(make-local-variable ',(car x))) binds) + (let (,@binds) + ,@body)) + (when (buffer-live-p ,buffer) + (with-current-buffer ,buffer + ,@(mapcar (lambda (x) + `(unless ,(car x) + (kill-local-variable ',(cdr x)))) + local))))))) + +(defvar consult--fast-abbreviate-file-name nil) +(defun consult--fast-abbreviate-file-name (name) + "Return abbreviate file NAME. +This function is a pure variant of `abbreviate-file-name', which +does not access the file system. This is important if we require +that the operation is fast, even for remote paths or paths on +network file systems." + (save-match-data + (let (case-fold-search) ;; Assume that file system is case sensitive. + (setq name (directory-abbrev-apply name)) + (if (string-match (with-memoization consult--fast-abbreviate-file-name + (directory-abbrev-make-regexp (expand-file-name "~"))) + name) + (concat "~" (substring name (match-beginning 1))) + name)))) + +(defun consult--left-truncate-file (file) + "Return abbreviated file name of FILE for use in `completing-read' prompt." + (save-match-data + (let ((afile (abbreviate-file-name file))) + (if (string-match "/\\([^/]+\\)/\\([^/]+/?\\)\\'" afile) + (propertize (format "…/%s/%s" (match-string 1 afile) (match-string 2 afile)) + 'help-echo afile) + afile)))) + +(defun consult--directory-prompt (prompt dir) + "Return prompt, paths and default directory. + +PROMPT is the prompt prefix. The directory is appended to the +prompt prefix. For projects only the project name is shown. The +`default-directory' is not shown. Other directories are +abbreviated and only the last two path components are shown. + +If DIR is a string, it is returned as default directory. If DIR +is a list of strings, the list is returned as search paths. If +DIR is nil the `consult-project-function' is tried to retrieve +the default directory. If no project is found the +`default-directory' is returned as is. Otherwise the user is +asked for the directories or files to search via +`completing-read-multiple'." + (let* ((paths nil) + (dir + (pcase dir + ((pred stringp) dir) + ('nil (or (consult--project-root) default-directory)) + (_ + (pcase (if (stringp (car-safe dir)) + dir + ;; Preserve this-command across `completing-read-multiple' call, + ;; such that `consult-customize' continues to work. + (let ((this-command this-command) + (def (abbreviate-file-name default-directory)) + ;; TODO: `minibuffer-completing-file-name' is + ;; mostly deprecated, but still in use. Packages + ;; should instead use the completion metadata. + (minibuffer-completing-file-name t) + (ignore-case read-file-name-completion-ignore-case)) + (consult--minibuffer-with-setup-hook + (lambda () + (setq-local completion-ignore-case ignore-case) + (set-syntax-table minibuffer-local-filename-syntax)) + (mapcar #'substitute-in-file-name + (completing-read-multiple "Directories or files: " + #'read-file-name-internal + nil t def 'consult--path-history def))))) + ((and `(,p) (guard (file-directory-p p))) p) + (ps (setq paths (mapcar (lambda (p) + (file-relative-name (expand-file-name p))) + ps)) + default-directory))))) + (edir (file-name-as-directory (expand-file-name dir))) + (pdir (let ((default-directory edir)) + ;; Bind default-directory in order to find the project + (consult--project-root)))) + (list + (format "%s (%s): " prompt + (pcase paths + (`(,p) (consult--left-truncate-file p)) + (`(,p . ,_) + (format "%d paths, %s, …" (length paths) (consult--left-truncate-file p))) + ((guard (equal edir pdir)) (concat "Project " (consult--project-name pdir))) + (_ (consult--left-truncate-file edir)))) + (or paths '(".")) + edir))) + +(defun consult--default-project-function (may-prompt) + "Return project root directory. +When no project is found and MAY-PROMPT is non-nil ask the user." + (when-let (proj (project-current may-prompt)) + (cond + ((fboundp 'project-root) (project-root proj)) + ((fboundp 'project-roots) (car (project-roots proj)))))) + +(defun consult--project-root (&optional may-prompt) + "Return project root as absolute path. +When no project is found and MAY-PROMPT is non-nil ask the user." + ;; Preserve this-command across project selection, + ;; such that `consult-customize' continues to work. + (let ((this-command this-command)) + (when-let (root (and consult-project-function + (funcall consult-project-function may-prompt))) + (expand-file-name root)))) + +(defun consult--project-name (dir) + "Return the project name for DIR." + (if (string-match "/\\([^/]+\\)/\\'" dir) + (propertize (match-string 1 dir) 'help-echo (abbreviate-file-name dir)) + dir)) + +(defun consult--format-file-line-match (file line match) + "Format string FILE:LINE:MATCH with faces." + (setq line (number-to-string line) + match (concat file ":" line ":" match) + file (length file)) + (put-text-property 0 file 'face 'consult-file match) + (put-text-property (1+ file) (+ 1 file (length line)) 'face 'consult-line-number match) + match) + +(defun consult--make-overlay (beg end &rest props) + "Make consult overlay between BEG and END with PROPS." + (let ((ov (make-overlay beg end))) + (while props + (overlay-put ov (car props) (cadr props)) + (setq props (cddr props))) + ov)) + +(defun consult--remove-dups (list) + "Remove duplicate strings from LIST." + (delete-dups (copy-sequence list))) + +(defsubst consult--in-range-p (pos) + "Return t if position POS lies in range `point-min' to `point-max'." + (<= (point-min) pos (point-max))) + +(defun consult--completion-window-p () + "Return non-nil if the selected window belongs to the completion UI." + (or (eq (selected-window) (active-minibuffer-window)) + (eq #'completion-list-mode (buffer-local-value 'major-mode (window-buffer))))) + +(defun consult--original-window () + "Return window which was just selected just before the minibuffer was entered. +In contrast to `minibuffer-selected-window' never return nil and +always return an appropriate non-minibuffer window." + (or (minibuffer-selected-window) + (if (window-minibuffer-p (selected-window)) + (next-window) + (selected-window)))) + +(defun consult--forbid-minibuffer () + "Raise an error if executed from the minibuffer." + (when (minibufferp) + (user-error "`%s' called inside the minibuffer" this-command))) + +(defun consult--require-minibuffer () + "Raise an error if executed outside the minibuffer." + (unless (minibufferp) + (user-error "`%s' must be called inside the minibuffer" this-command))) + +(defun consult--fontify-all () + "Ensure that the whole buffer is fontified." + ;; Font-locking is lazy, i.e., if a line has not been looked at yet, the line + ;; is not font-locked. We would observe this if consulting an unfontified + ;; line. Therefore we have to enforce font-locking now, which is slow. In + ;; order to prevent is hang-up we check the buffer size against + ;; `consult-fontify-max-size'. + (when (and consult-fontify-preserve jit-lock-mode + (< (buffer-size) consult-fontify-max-size)) + (jit-lock-fontify-now))) + +(defun consult--fontify-region (start end) + "Ensure that region between START and END is fontified." + (when (and consult-fontify-preserve jit-lock-mode) + (jit-lock-fontify-now start end))) + +(defmacro consult--with-increased-gc (&rest body) + "Temporarily increase the GC limit in BODY to optimize for throughput." + (cl-with-gensyms (overwrite) + `(let* ((,overwrite (> consult--gc-threshold gc-cons-threshold)) + (gc-cons-threshold (if ,overwrite consult--gc-threshold gc-cons-threshold)) + (gc-cons-percentage (if ,overwrite consult--gc-percentage gc-cons-percentage))) + ,@body))) + +(defmacro consult--slow-operation (message &rest body) + "Show delayed MESSAGE if BODY takes too long. +Also temporarily increase the GC limit via `consult--with-increased-gc'." + (declare (indent 1)) + `(let (set-message-function) ;; bug#63253: Broken `with-delayed-message' + (with-delayed-message (1 ,message) + (consult--with-increased-gc + ,@body)))) + +(defun consult--count-lines (pos) + "Move to position POS and return number of lines." + (let ((line 1)) + (while (< (point) pos) + (forward-line) + (when (<= (point) pos) + (cl-incf line))) + (goto-char pos) + line)) + +(defun consult--marker-from-line-column (buffer line column) + "Get marker in BUFFER from LINE and COLUMN." + (when (buffer-live-p buffer) + (with-current-buffer buffer + (save-excursion + (without-restriction + (goto-char (point-min)) + ;; Location data might be invalid by now! + (ignore-errors + (forward-line (1- line)) + (goto-char (min (+ (point) column) (pos-eol)))) + (point-marker)))))) + +(defun consult--line-prefix (&optional curr-line) + "Annotate `consult-location' candidates with line numbers. +CURR-LINE is the current line number." + (setq curr-line (or curr-line -1)) + (let* ((width (length (number-to-string (line-number-at-pos + (point-max) + consult-line-numbers-widen)))) + (before (format #("%%%dd " 0 6 (face consult-line-number-wrapped)) width)) + (after (format #("%%%dd " 0 6 (face consult-line-number-prefix)) width))) + (lambda (cand) + (let ((line (cdr (get-text-property 0 'consult-location cand)))) + (list cand (format (if (< line curr-line) before after) line) ""))))) + +(defsubst consult--location-candidate (cand marker line tofu &rest props) + "Add MARKER and LINE as `consult-location' text property to CAND. +Furthermore add the additional text properties PROPS, and append +TOFU suffix for disambiguation." + (setq cand (concat cand (consult--tofu-encode tofu))) + (add-text-properties 0 1 `(consult-location (,marker . ,line) ,@props) cand) + cand) + +;; There is a similar variable `yank-excluded-properties'. Unfortunately +;; we cannot use it here since it excludes too much (e.g., invisible) +;; and at the same time not enough (e.g., cursor-sensor-functions). +(defconst consult--remove-text-properties + '(category cursor cursor-intangible cursor-sensor-functions field follow-link + fontified front-sticky help-echo insert-behind-hooks insert-in-front-hooks + intangible keymap local-map modification-hooks mouse-face pointer read-only + rear-nonsticky yank-handler) + "List of text properties to remove from buffer strings.") + +(defsubst consult--buffer-substring (beg end &optional fontify) + "Return buffer substring between BEG and END. +If FONTIFY and `consult-fontify-preserve' are non-nil, first ensure that the +region has been fontified." + (if consult-fontify-preserve + (let (str) + (when fontify (consult--fontify-region beg end)) + (setq str (buffer-substring beg end)) + ;; TODO Propose the upstream addition of a function + ;; `preserve-list-of-text-properties', which should be as efficient as + ;; `remove-list-of-text-properties'. + (remove-list-of-text-properties + 0 (- end beg) consult--remove-text-properties str) + str) + (buffer-substring-no-properties beg end))) + +(defun consult--line-with-mark (marker) + "Current line string where the MARKER position is highlighted." + (let* ((beg (pos-bol)) + (end (pos-eol)) + (str (consult--buffer-substring beg end 'fontify))) + (if (>= marker end) + (concat str #(" " 0 1 (face consult-highlight-mark))) + (put-text-property (- marker beg) (- (1+ marker) beg) + 'face 'consult-highlight-mark str) + str))) + +;;;; Tofu cooks + +(defsubst consult--tofu-p (char) + "Return non-nil if CHAR is a tofu." + (<= consult--tofu-char char (+ consult--tofu-char consult--tofu-range -1))) + +(defun consult--tofu-hide (str) + "Hide the tofus in STR." + (let* ((max (length str)) + (end max)) + (while (and (> end 0) (consult--tofu-p (aref str (1- end)))) + (cl-decf end)) + (when (< end max) + (setq str (copy-sequence str)) + (put-text-property end max 'invisible t str)) + str)) + +(defsubst consult--tofu-append (cand id) + "Append tofu-encoded ID to CAND. +The ID must fit within a single character. It must be smaller +than `consult--tofu-range'." + (setq id (char-to-string (+ consult--tofu-char id))) + (add-text-properties 0 1 '(invisible t consult-strip t) id) + (concat cand id)) + +(defsubst consult--tofu-get (cand) + "Extract tofu-encoded ID from CAND. +See `consult--tofu-append'." + (- (aref cand (1- (length cand))) consult--tofu-char)) + +;; We must disambiguate the lines by adding a prefix such that two lines with +;; the same text can be distinguished. In order to avoid matching the line +;; number, such that the user can search for numbers with `consult-line', we +;; encode the line number as characters outside the Unicode range. By doing +;; that, no accidental matching can occur. +(defun consult--tofu-encode (n) + "Return tofu-encoded number N as a string. +Large numbers are encoded as multiple tofu characters." + (let (str tofu) + (while (progn + (setq tofu (char-to-string + (+ consult--tofu-char (% n consult--tofu-range))) + str (if str (concat tofu str) tofu)) + (and (>= n consult--tofu-range) + (setq n (/ n consult--tofu-range))))) + (add-text-properties 0 (length str) '(invisible t consult-strip t) str) + str)) + +;;;; Regexp utilities + +(defun consult--find-highlights (str start &rest ignored-faces) + "Find highlighted regions in STR from position START. +Highlighted regions have a non-nil face property. +IGNORED-FACES are ignored when searching for matches." + (let (highlights + (end (length str)) + (beg start)) + (while (< beg end) + (let ((next (next-single-property-change beg 'face str end)) + (val (get-text-property beg 'face str))) + (when (and val + (not (memq val ignored-faces)) + (not (and (consp val) + (seq-some (lambda (x) (memq x ignored-faces)) val)))) + (push (cons (- beg start) (- next start)) highlights)) + (setq beg next))) + (nreverse highlights))) + +(defun consult--point-placement (str start &rest ignored-faces) + "Compute point placement from STR with START offset. +IGNORED-FACES are ignored when searching for matches. +Return cons of point position and a list of match begin/end pairs." + (let* ((matches (apply #'consult--find-highlights str start ignored-faces)) + (pos (pcase-exhaustive consult-point-placement + ('match-beginning (or (caar matches) 0)) + ('match-end (or (cdar (last matches)) 0)) + ('line-beginning 0)))) + (dolist (match matches) + (cl-decf (car match) pos) + (cl-decf (cdr match) pos)) + (cons pos matches))) + +(defun consult--highlight-regexps (regexps ignore-case str) + "Highlight REGEXPS in STR. +If a regular expression contains capturing groups, only these are highlighted. +If no capturing groups are used highlight the whole match. Case is ignored +if IGNORE-CASE is non-nil." + (dolist (re regexps) + (let ((i 0)) + (while (and (let ((case-fold-search ignore-case)) + (string-match re str i)) + ;; Ensure that regexp search made progress (edge case for .*) + (> (match-end 0) i)) + ;; Unfortunately there is no way to avoid the allocation of the match + ;; data, since the number of capturing groups is unknown. + (let ((m (match-data))) + (setq i (cadr m) m (or (cddr m) m)) + (while m + (when (car m) + (add-face-text-property (car m) (cadr m) + 'consult-highlight-match nil str)) + (setq m (cddr m))))))) + str) + +(defconst consult--convert-regexp-table + (append + ;; For simplicity, treat word beginning/end as word boundaries, + ;; since PCRE does not make this distinction. Usually the + ;; context determines if \b is the beginning or the end. + '(("\\<" . "\\b") ("\\>" . "\\b") + ("\\_<" . "\\b") ("\\_>" . "\\b")) + ;; Treat \` and \' as beginning and end of line. This is more + ;; widely supported and makes sense for line-based commands. + '(("\\`" . "^") ("\\'" . "$")) + ;; Historical: Unescaped *, +, ? are supported at the beginning + (mapcan (lambda (x) + (mapcar (lambda (y) + (cons (concat x y) + (concat (string-remove-prefix "\\" x) "\\" y))) + '("*" "+" "?"))) + '("" "\\(" "\\(?:" "\\|" "^")) + ;; Different escaping + (mapcan (lambda (x) `(,x (,(cdr x) . ,(car x)))) + '(("\\|" . "|") + ("\\(" . "(") ("\\)" . ")") + ("\\{" . "{") ("\\}" . "}")))) + "Regexp conversion table.") + +(defun consult--convert-regexp (regexp type) + "Convert Emacs REGEXP to regexp syntax TYPE." + (if (memq type '(emacs basic)) + regexp + ;; Support for Emacs regular expressions is fairly complete for basic + ;; usage. There are a few unsupported Emacs regexp features: + ;; - \= point matching + ;; - Syntax classes \sx \Sx + ;; - Character classes \cx \Cx + ;; - Explicitly numbered groups (?3:group) + (replace-regexp-in-string + (rx (or "\\\\" "\\^" ;; Pass through + (seq (or "\\(?:" "\\|") (any "*+?")) ;; Historical: \|+ or \(?:* etc + (seq "\\(" (any "*+")) ;; Historical: \(* or \(+ + (seq (or bos "^") (any "*+?")) ;; Historical: + or * at the beginning + (seq (opt "\\") (any "(){|}")) ;; Escape parens/braces/pipe + (seq "\\" (any "'<>`")) ;; Special escapes + (seq "\\_" (any "<>")))) ;; Beginning or end of symbol + (lambda (x) (or (cdr (assoc x consult--convert-regexp-table)) x)) + regexp 'fixedcase 'literal))) + +(defun consult--default-regexp-compiler (input type ignore-case) + "Compile the INPUT string to a list of regular expressions. +The function should return a pair, the list of regular expressions and a +highlight function. The highlight function should take a single +argument, the string to highlight given the INPUT. TYPE is the desired +type of regular expression, which can be `basic', `extended', `emacs' or +`pcre'. If IGNORE-CASE is non-nil return a highlight function which +matches case insensitively." + (setq input (consult--split-escaped input)) + (cons (mapcar (lambda (x) (consult--convert-regexp x type)) input) + (when-let (regexps (seq-filter #'consult--valid-regexp-p input)) + (apply-partially #'consult--highlight-regexps regexps ignore-case)))) + +(defun consult--split-escaped (str) + "Split STR at spaces, which can be escaped with backslash." + (mapcar + (lambda (x) (string-replace "\0" " " x)) + (split-string (replace-regexp-in-string + "\\\\\\\\\\|\\\\ " + (lambda (x) (if (equal x "\\ ") "\0" x)) + str 'fixedcase 'literal) + " +" t))) + +(defun consult--join-regexps (regexps type) + "Join REGEXPS of TYPE." + ;; Add look-ahead wrapper only if there is more than one regular expression + (cond + ((and (eq type 'pcre) (cdr regexps)) + (concat "^" (mapconcat (lambda (x) (format "(?=.*%s)" x)) + regexps ""))) + ((eq type 'basic) + (string-join regexps ".*")) + (t + (when (length> regexps 3) + (message "Too many regexps, %S ignored. Use post-filtering!" + (string-join (seq-drop regexps 3) " ")) + (setq regexps (seq-take regexps 3))) + (consult--join-regexps-permutations regexps (and (eq type 'emacs) "\\"))))) + +(defun consult--join-regexps-permutations (regexps esc) + "Join all permutations of REGEXPS. +ESC is the escaping string for choice and groups." + (pcase regexps + ('nil "") + (`(,r) r) + (_ (mapconcat + (lambda (r) + (concat esc "(" r esc ").*" esc "(" + (consult--join-regexps-permutations (remove r regexps) esc) + esc ")")) + regexps (concat esc "|"))))) + +(defun consult--valid-regexp-p (re) + "Return t if regexp RE is valid." + (condition-case nil + (progn (string-match-p re "") t) + (invalid-regexp nil))) + +(defun consult--regexp-filter (regexps) + "Create filter regexp from REGEXPS." + (if (stringp regexps) + regexps + (mapconcat (lambda (x) (concat "\\(?:" x "\\)")) regexps "\\|"))) + +;;;; Lookup functions + +(defun consult--lookup-member (selected candidates &rest _) + "Lookup SELECTED in CANDIDATES list, return original element." + (car (member selected candidates))) + +(defun consult--lookup-cons (selected candidates &rest _) + "Lookup SELECTED in CANDIDATES alist, return cons." + (assoc selected candidates)) + +(defun consult--lookup-cdr (selected candidates &rest _) + "Lookup SELECTED in CANDIDATES alist, return `cdr' of element." + (cdr (assoc selected candidates))) + +(defun consult--lookup-location (selected candidates &rest _) + "Lookup SELECTED in CANDIDATES list of `consult-location' category. +Return the location marker." + (when-let (found (member selected candidates)) + (setq found (car (consult--get-location (car found)))) + ;; Check that marker is alive + (and (or (not (markerp found)) (marker-buffer found)) found))) + +(defun consult--lookup-prop (prop selected candidates &rest _) + "Lookup SELECTED in CANDIDATES list and return PROP value." + (when-let (found (member selected candidates)) + (get-text-property 0 prop (car found)))) + +(defun consult--lookup-candidate (selected candidates &rest _) + "Lookup SELECTED in CANDIDATES list and return property `consult--candidate'." + (consult--lookup-prop 'consult--candidate selected candidates)) + +;;;; Preview support + +(defun consult--preview-allowed-p (fun) + "Return non-nil if FUN is an allowed preview mode hook." + (or (memq fun consult-preview-allowed-hooks) + (when-let (((symbolp fun)) + (name (symbol-name fun)) + ;; Global modes in Emacs 29 are activated via a + ;; `find-file-hook' ending with `-check-buffers'. This has been + ;; changed in Emacs 30. Now a `change-major-mode-hook' is used + ;; instead with the suffix `-check-buffers'. + (suffix (static-if (>= emacs-major-version 30) + "-enable-in-buffer" + "-check-buffers")) + ((string-suffix-p suffix name))) + (memq (intern (string-remove-suffix suffix name)) + consult-preview-allowed-hooks)))) + +(defun consult--filter-find-file-hook (orig &rest hooks) + "Filter `find-file-hook' by `consult-preview-allowed-hooks'. +This function is an advice for `run-hooks'. +ORIG is the original function, HOOKS the arguments." + (if (memq 'find-file-hook hooks) + (cl-letf* (((default-value 'find-file-hook) + (seq-filter #'consult--preview-allowed-p + (default-value 'find-file-hook))) + (find-file-hook (default-value 'find-file-hook))) + (apply orig hooks)) + (apply orig hooks))) + +(defun consult--find-file-temporarily-1 (name) + "Open file NAME, helper function for `consult--find-file-temporarily'." + (when-let (((not (seq-find (lambda (x) (string-match-p x name)) + consult-preview-excluded-files))) + ;; file-attributes may throw permission denied error + (attrs (ignore-errors (file-attributes name))) + (size (file-attribute-size attrs))) + (let* ((partial (>= size consult-preview-partial-size)) + (buffer (if partial + (generate-new-buffer (format "consult-partial-preview-%s" name)) + (find-file-noselect name 'nowarn))) + (success nil)) + (unwind-protect + (with-current-buffer buffer + (if (not partial) + (when (or (eq major-mode 'hexl-mode) + (and (eq major-mode 'fundamental-mode) + (save-excursion (search-forward "\0" nil 'noerror)))) + (error "No preview of binary file `%s'" + (file-name-nondirectory name))) + (with-silent-modifications + (setq buffer-read-only t) + (insert-file-contents name nil 0 consult-preview-partial-chunk) + (goto-char (point-max)) + (insert "\nFile truncated. End of partial preview.\n") + (goto-char (point-min))) + (when (save-excursion (search-forward "\0" nil 'noerror)) + (error "No partial preview of binary file `%s'" + (file-name-nondirectory name))) + ;; Auto detect major mode and hope for the best, given that the + ;; file is only previewed partially. If an error is thrown the + ;; buffer will be killed and preview is aborted. + (set-auto-mode) + (font-lock-mode 1)) + (when (bound-and-true-p so-long-detected-p) + (error "No preview of file `%s' with long lines" + (file-name-nondirectory name))) + ;; Run delayed hooks listed in `consult-preview-allowed-hooks'. + (dolist (hook (reverse (cons 'after-change-major-mode-hook delayed-mode-hooks))) + (run-hook-wrapped hook (lambda (fun) + (when (consult--preview-allowed-p fun) + (funcall fun)) + nil))) + (setq success (current-buffer))) + (unless success + (kill-buffer buffer)))))) + +(defun consult--find-file-temporarily (name) + "Open file NAME temporarily for preview." + (let ((vars (delq nil + (mapcar + (pcase-lambda (`(,k . ,v)) + (if (boundp k) + (list k v (default-value k) (symbol-value k)) + (message "consult-preview-variables: The variable `%s' is not bound" k) + nil)) + consult-preview-variables)))) + (condition-case err + (unwind-protect + (progn + (advice-add #'run-hooks :around #'consult--filter-find-file-hook) + (pcase-dolist (`(,k ,v . ,_) vars) + (set-default k v) + (set k v)) + (consult--find-file-temporarily-1 name)) + (advice-remove #'run-hooks #'consult--filter-find-file-hook) + (pcase-dolist (`(,k ,_ ,d ,v) vars) + (set-default k d) + (set k v))) + (error + (message "%s" (error-message-string err)) + nil)))) + +(defun consult--temporary-files () + "Return a function to open files temporarily for preview." + (let ((dir default-directory) + (hook (make-symbol "consult--temporary-files-upgrade-hook")) + (orig-buffers (buffer-list)) + temporary-buffers) + (fset hook + (lambda (_) + ;; Fully initialize previewed files and keep them alive. + (unless (consult--completion-window-p) + (let (live-files) + (pcase-dolist (`(,file . ,buf) temporary-buffers) + (when-let (wins (and (buffer-live-p buf) + (get-buffer-window-list buf))) + (push (cons file (mapcar + (lambda (win) + (cons win (window-state-get win t))) + wins)) + live-files))) + (pcase-dolist (`(,_ . ,buf) temporary-buffers) + (kill-buffer buf)) + (setq temporary-buffers nil) + (pcase-dolist (`(,file . ,wins) live-files) + (when-let (buf (consult--file-action file)) + (push buf orig-buffers) + (pcase-dolist (`(,win . ,state) wins) + (setf (car (alist-get 'buffer state)) buf) + (window-state-put state win)))))))) + (lambda (&optional name) + (if name + (let ((default-directory dir)) + (setq name (abbreviate-file-name (expand-file-name name))) + (or + ;; Find existing fully initialized buffer (non-previewed). We have + ;; to check for fully initialized buffer before accessing the + ;; previewed buffers, since `embark-act' can open a buffer which is + ;; currently previewed, such that we end up with two buffers for + ;; the same file - one previewed and only partially initialized and + ;; one fully initialized. In this case we prefer the fully + ;; initialized buffer. For directories `get-file-buffer' returns nil, + ;; therefore we have to special case Dired. + (if (and (fboundp 'dired-find-buffer-nocreate) (file-directory-p name)) + (dired-find-buffer-nocreate name) + (get-file-buffer name)) + ;; Find existing previewed buffer. Previewed buffers are not fully + ;; initialized (hooks are delayed) in order to ensure fast preview. + (cdr (assoc name temporary-buffers)) + ;; Finally, if no existing buffer has been found, open the file for + ;; preview. + (when-let (buf (consult--find-file-temporarily name)) + ;; Only add new buffer if not already in the list + (unless (or (rassq buf temporary-buffers) (memq buf orig-buffers)) + (add-hook 'window-selection-change-functions hook) + (push (cons name buf) temporary-buffers) + ;; Disassociate buffer from file by setting `buffer-file-name' + ;; and `dired-directory' to nil and rename the buffer. This + ;; lets us open an already previewed buffer with the Embark + ;; default action C-. RET. + (with-current-buffer buf + (rename-buffer + (format " Preview:%s" + (file-name-nondirectory (directory-file-name name))) + 'unique)) + ;; The buffer disassociation is delayed to avoid breaking modes + ;; like `pdf-view-mode' or `doc-view-mode' which rely on + ;; `buffer-file-name'. Executing (set-visited-file-name nil) + ;; early also prevents the major mode initialization. + (let ((hook (make-symbol "consult--temporary-files-disassociate-hook"))) + (fset hook (lambda () + (when (buffer-live-p buf) + (with-current-buffer buf + (remove-hook 'pre-command-hook hook) + (setq-local buffer-read-only t + dired-directory nil + buffer-file-name nil))))) + (add-hook 'pre-command-hook hook)) + ;; Only keep a few buffers alive + (while (length> temporary-buffers consult-preview-max-count) + (kill-buffer (cdar (last temporary-buffers))) + (setq temporary-buffers (nbutlast temporary-buffers)))) + buf))) + (remove-hook 'window-selection-change-functions hook) + (pcase-dolist (`(,_ . ,buf) temporary-buffers) + (kill-buffer buf)) + (setq temporary-buffers nil))))) + +(defun consult--invisible-open-permanently () + "Open overlays which hide the current line. +See `isearch-open-necessary-overlays' and `isearch-open-overlay-temporary'." + (if (and (derived-mode-p 'org-mode) (fboundp 'org-fold-show-set-visibility)) + ;; New Org 9.6 fold-core API + (let ((inhibit-redisplay t)) ;; HACK: Prevent flicker due to premature redisplay + (org-fold-show-set-visibility 'canonical)) + (dolist (ov (overlays-in (pos-bol) (pos-eol))) + (when-let (fun (overlay-get ov 'isearch-open-invisible)) + (when (invisible-p (overlay-get ov 'invisible)) + (funcall fun ov)))))) + +(defun consult--invisible-open-temporarily () + "Temporarily open overlays which hide the current line. +See `isearch-open-necessary-overlays' and `isearch-open-overlay-temporary'." + (if (and (derived-mode-p 'org-mode) + (fboundp 'org-fold-show-set-visibility) + (fboundp 'org-fold-core-get-regions) + (fboundp 'org-fold-core-region)) + ;; New Org 9.6 fold-core API + ;; TODO The provided Org API `org-fold-show-set-visibility' cannot be used + ;; efficiently. We obtain all regions in the whole buffer in order to + ;; restore them. A better show API would return all the applied + ;; modifications such that we can restore the ones which got modified. + (progn + (unless consult--org-fold-regions + (setq consult--org-fold-regions + (delq nil (org-fold-core-get-regions + :with-markers t :from (point-min) :to (point-max)))) + (when consult--org-fold-regions + (let ((hook (make-symbol "consult--invisible-open-temporarily-cleanup-hook")) + (buffer (current-buffer)) + (depth (recursion-depth))) + (fset hook + (lambda () + (when (= (recursion-depth) depth) + (remove-hook 'minibuffer-exit-hook hook) + (run-at-time + 0 nil + (lambda () + (when (buffer-live-p buffer) + (with-current-buffer buffer + (pcase-dolist (`(,beg ,end ,_) consult--org-fold-regions) + (when (markerp beg) (set-marker beg nil)) + (when (markerp end) (set-marker end nil))) + (kill-local-variable 'consult--org-fold-regions)))))))) + (add-hook 'minibuffer-exit-hook hook)))) + (let ((inhibit-redisplay t)) ;; HACK: Prevent flicker due to premature redisplay + (org-fold-show-set-visibility 'canonical)) + (list (lambda () + (pcase-dolist (`(,beg ,end ,spec) consult--org-fold-regions) + (org-fold-core-region beg end t spec))))) + (let (restore) + (dolist (ov (overlays-in (pos-bol) (pos-eol))) + (let ((inv (overlay-get ov 'invisible))) + (when (and (invisible-p inv) (overlay-get ov 'isearch-open-invisible)) + (push (if-let (fun (overlay-get ov 'isearch-open-invisible-temporary)) + (progn + (funcall fun ov nil) + (lambda () (funcall fun ov t))) + (overlay-put ov 'invisible nil) + (lambda () (overlay-put ov 'invisible inv))) + restore)))) + restore))) + +(defun consult--jump-ensure-buffer (pos) + "Ensure that buffer of marker POS is displayed, return t if successful." + (or (not (markerp pos)) + ;; Switch to buffer if it is not visible + (when-let ((buf (marker-buffer pos))) + (or (and (eq (current-buffer) buf) (eq (window-buffer) buf)) + (consult--buffer-action buf 'norecord) + t)))) + +(defun consult--jump (pos) + "Jump to POS. +First push current position to mark ring, then move to new +position and run `consult-after-jump-hook'." + (when pos + ;; Extract marker from list with with overlay positions, see `consult--line-match' + (when (consp pos) (setq pos (car pos))) + ;; When the marker is in the same buffer, record previous location + ;; such that the user can jump back quickly. + (when (or (not (markerp pos)) (eq (current-buffer) (marker-buffer pos))) + ;; push-mark mutates markers in the mark-ring and the mark-marker. + ;; Therefore we transform the marker to a number to be safe. + ;; We all love side effects! + (setq pos (+ pos 0)) + (push-mark (point) t)) + (when (consult--jump-ensure-buffer pos) + (unless (= (goto-char pos) (point)) ;; Widen if jump failed + (widen) + (goto-char pos)) + (consult--invisible-open-permanently) + (run-hooks 'consult-after-jump-hook))) + nil) + +(defun consult--jump-preview () + "The preview function used if selecting from a list of candidate positions. +The function can be used as the `:state' argument of `consult--read'." + (let (restore) + (lambda (action cand) + (when (eq action 'preview) + (mapc #'funcall restore) + (setq restore nil) + ;; TODO Better buffer preview support + ;; 1. Use consult--buffer-preview instead of consult--jump-ensure-buffer + ;; 2. Remove function consult--jump-ensure-buffer + ;; 3. Remove consult-buffer-other-* from consult-customize-alist + (when-let ((pos (or (car-safe cand) cand)) ;; Candidate can be previewed + ((consult--jump-ensure-buffer pos))) + (let ((saved-min (point-min-marker)) + (saved-max (point-max-marker)) + (saved-pos (point-marker))) + (set-marker-insertion-type saved-max t) ;; Grow when text is inserted + (push (lambda () + (when-let ((buf (marker-buffer saved-pos))) + (with-current-buffer buf + (narrow-to-region saved-min saved-max) + (goto-char saved-pos) + (set-marker saved-pos nil) + (set-marker saved-min nil) + (set-marker saved-max nil)))) + restore)) + (unless (= (goto-char pos) (point)) ;; Widen if jump failed + (widen) + (goto-char pos)) + (setq restore (nconc (consult--invisible-open-temporarily) restore)) + ;; Ensure that cursor is properly previewed (gh:minad/consult#764) + (unless (eq cursor-in-non-selected-windows 'box) + (let ((orig cursor-in-non-selected-windows) + (buf (current-buffer))) + (push + (if (local-variable-p 'cursor-in-non-selected-windows) + (lambda () + (when (buffer-live-p buf) + (with-current-buffer buf + (setq-local cursor-in-non-selected-windows orig)))) + (lambda () + (when (buffer-live-p buf) + (with-current-buffer buf + (kill-local-variable 'cursor-in-non-selected-windows))))) + restore) + (setq-local cursor-in-non-selected-windows 'box))) + ;; Match previews + (let ((overlays + (list (save-excursion + (let ((vbeg (progn (beginning-of-visual-line) (point))) + (vend (progn (end-of-visual-line) (point))) + (end (pos-eol))) + (consult--make-overlay vbeg (if (= vend end) (1+ end) vend) + 'face 'consult-preview-line + 'window (selected-window) + 'priority 1)))))) + (dolist (match (cdr-safe cand)) + (push (consult--make-overlay (+ (point) (car match)) + (+ (point) (cdr match)) + 'face 'consult-preview-match + 'window (selected-window) + 'priority 2) + overlays)) + (push (lambda () (mapc #'delete-overlay overlays)) restore)) + (run-hooks 'consult-after-jump-hook)))))) + +(defun consult--jump-state () + "The state function used if selecting from a list of candidate positions." + (consult--state-with-return (consult--jump-preview) #'consult--jump)) + +(defun consult--get-location (cand) + "Return location from CAND." + (let ((loc (get-text-property 0 'consult-location cand))) + (when (consp (car loc)) + ;; Transform cheap marker to real marker + (setcar loc (set-marker (make-marker) (cdar loc) (caar loc)))) + loc)) + +(defun consult--location-state (candidates) + "Location state function. +The cheap location markers from CANDIDATES are upgraded on window +selection change to full Emacs markers." + (let ((jump (consult--jump-state)) + (hook (make-symbol "consult--location-upgrade-hook"))) + (fset hook + (lambda (_) + (unless (consult--completion-window-p) + (remove-hook 'window-selection-change-functions hook) + (mapc #'consult--get-location + (if (functionp candidates) (funcall candidates) candidates))))) + (lambda (action cand) + (pcase action + ('setup (add-hook 'window-selection-change-functions hook)) + ('exit (remove-hook 'window-selection-change-functions hook))) + (funcall jump action cand)))) + +(defun consult--state-with-return (state return) + "Compose STATE function with RETURN function." + (lambda (action cand) + (funcall state action cand) + (when (and cand (eq action 'return)) + (funcall return cand)))) + +(defmacro consult--define-state (type) + "Define state function for TYPE." + `(defun ,(intern (format "consult--%s-state" type)) () + ,(format "State function for %ss with preview. +The result can be passed as :state argument to `consult--read'." type) + (consult--state-with-return (,(intern (format "consult--%s-preview" type))) + #',(intern (format "consult--%s-action" type))))) + +(defun consult--preview-key-normalize (preview-key) + "Normalize PREVIEW-KEY, return alist of keys and debounce times." + (let ((keys) + (debounce 0)) + (setq preview-key (ensure-list preview-key)) + (while preview-key + (if (eq (car preview-key) :debounce) + (setq debounce (cadr preview-key) + preview-key (cddr preview-key)) + (let ((key (car preview-key))) + (unless (eq key 'any) + (setq key (consult--key-parse key))) + (push (cons key debounce) keys)) + (pop preview-key))) + keys)) + +(defun consult--preview-key-debounce (preview-key cand) + "Return debounce value of PREVIEW-KEY given the current candidate CAND." + (when (and (consp preview-key) (memq :keys preview-key)) + (setq preview-key (funcall (plist-get preview-key :predicate) cand))) + (let ((map (make-sparse-keymap)) + (keys (this-single-command-keys)) + any) + (pcase-dolist (`(,k . ,d) (consult--preview-key-normalize preview-key)) + (if (eq k 'any) + (setq any d) + (define-key map k `(lambda () ,d)))) + (setq keys (lookup-key map keys)) + (if (functionp keys) (funcall keys) any))) + +(defun consult--preview-append-local-pch (fun) + "Append FUN to local `post-command-hook' list." + ;; Symbol indirection because of bug#46407. + (let ((hook (make-symbol "consult--preview-post-command-hook"))) + (fset hook fun) + ;; TODO Emacs 28 has a bug, where the hook--depth-alist is not cleaned up properly + ;; Do not use the broken add-hook here. + ;;(add-hook 'post-command-hook hook 'append 'local) + (setq-local post-command-hook + (append + (remove t post-command-hook) + (list hook) + (and (memq t post-command-hook) '(t)))))) + +(defun consult--with-preview-1 (preview-key state transform candidate save-input fun) + "Add preview support for FUN. +See `consult--with-preview' for the arguments +PREVIEW-KEY, STATE, TRANSFORM, CANDIDATE and SAVE-INPUT." + (let ((mb-input "") mb-narrow selected timer previewed) + (consult--minibuffer-with-setup-hook + (if (and state preview-key) + (lambda () + (let ((hook (make-symbol "consult--preview-minibuffer-exit-hook")) + (depth (recursion-depth))) + (fset hook + (lambda () + (when (= (recursion-depth) depth) + (remove-hook 'minibuffer-exit-hook hook) + (when timer + (cancel-timer timer) + (setq timer nil)) + (with-selected-window (consult--original-window) + ;; STEP 3: Reset preview + (when previewed + (funcall state 'preview nil)) + ;; STEP 4: Notify the preview function of the minibuffer exit + (funcall state 'exit nil))))) + (add-hook 'minibuffer-exit-hook hook)) + ;; STEP 1: Setup the preview function + (with-selected-window (consult--original-window) + (funcall state 'setup nil)) + (setq consult--preview-function + (lambda () + (when-let ((cand (funcall candidate))) + ;; Drop properties to prevent bugs regarding candidate + ;; lookup, which must handle candidates without + ;; properties. Otherwise the arguments passed to the + ;; lookup function are confusing, since during preview + ;; the candidate has properties but for the final lookup + ;; after completion it does not. + (setq cand (substring-no-properties cand)) + (with-selected-window (active-minibuffer-window) + (let ((input (minibuffer-contents-no-properties)) + (narrow consult--narrow) + (win (consult--original-window))) + (with-selected-window win + (when-let ((transformed (funcall transform narrow input cand)) + (debounce (consult--preview-key-debounce preview-key transformed))) + (when timer + (cancel-timer timer) + (setq timer nil)) + ;; The transformed candidate may have text + ;; properties, which change the preview display. + ;; This matters for example for `consult-grep', + ;; where the current candidate and input may + ;; stay equal, but the highlighting of the + ;; candidate changes while the candidates list + ;; is lagging a bit behind and updates + ;; asynchronously. + ;; + ;; In older Consult versions we instead compared + ;; the input without properties, since I worried + ;; that comparing the transformed candidates + ;; could be potentially expensive. However + ;; comparing the transformed candidates is more + ;; correct. The transformed candidate is the + ;; thing which is actually previewed. + (unless (equal-including-properties previewed transformed) + (if (> debounce 0) + (setq timer + (run-at-time + debounce nil + (lambda () + ;; Preview only when a completion + ;; window is selected and when + ;; the preview window is alive. + (when (and (consult--completion-window-p) + (window-live-p win)) + (with-selected-window win + ;; STEP 2: Preview candidate + (funcall state 'preview (setq previewed transformed))))))) + ;; STEP 2: Preview candidate + (funcall state 'preview (setq previewed transformed))))))))))) + (consult--preview-append-local-pch + (lambda () + (setq mb-input (minibuffer-contents-no-properties) + mb-narrow consult--narrow) + (funcall consult--preview-function)))) + (lambda () + (consult--preview-append-local-pch + (lambda () + (setq mb-input (minibuffer-contents-no-properties) + mb-narrow consult--narrow))))) + (unwind-protect + (setq selected (when-let (result (funcall fun)) + (when-let ((save-input) + (list (symbol-value save-input)) + ((equal (car list) result))) + (set save-input (cdr list))) + (funcall transform mb-narrow mb-input result))) + (when save-input + (add-to-history save-input mb-input)) + (when state + ;; STEP 5: The preview function should perform its final action + (funcall state 'return selected)))))) + +(defmacro consult--with-preview (preview-key state transform candidate save-input &rest body) + "Add preview support to BODY. + +STATE is the state function. +TRANSFORM is the transformation function. +CANDIDATE is the function returning the current candidate. +PREVIEW-KEY are the keys which triggers the preview. +SAVE-INPUT can be a history variable symbol to save the input. + +The state function takes two arguments, an action argument and the +selected candidate. The candidate argument can be nil if no candidate is +selected or if the selection was aborted. The function is called in +sequence with the following arguments: + + 1. \\='setup nil After entering the mb (minibuffer-setup-hook). +⎧ 2. \\='preview CAND/nil Preview candidate CAND or reset if CAND is nil. +⎪ \\='preview CAND/nil +⎪ \\='preview CAND/nil +⎪ ... +⎩ 3. \\='preview nil Reset preview. + 4. \\='exit nil Before exiting the mb (minibuffer-exit-hook). + 5. \\='return CAND/nil After leaving the mb, CAND has been selected. + +The state function is always executed with the original window selected, +see `consult--original-window'. The state function is called once in +the beginning of the minibuffer setup with the `setup' argument. This is +useful in order to perform certain setup operations which require that +the minibuffer is initialized. During completion candidates are +previewed. Then the function is called with the `preview' argument and a +candidate CAND or nil if no candidate is selected. Furthermore if nil is +passed for CAND, then the preview must be undone and the original state +must be restored. The call with the `exit' argument happens once at the +end of the completion process, just before exiting the minibuffer. The +minibuffer is still alive at that point. Both `setup' and `exit' are +only useful for setup and cleanup operations. They don't receive a +candidate as argument. After leaving the minibuffer, the selected +candidate or nil is passed to the state function with the action +argument `return'. At this point the state function can perform the +actual action on the candidate. The state function with the `return' +argument is the continuation of `consult--read'. Via `unwind-protect' it +is guaranteed, that if the `setup' action of a state function is +invoked, the state function will also be called with `exit' and +`return'." + (declare (indent 5)) + `(consult--with-preview-1 ,preview-key ,state ,transform ,candidate ,save-input (lambda () ,@body))) + +;;;; Narrowing and grouping + +(defun consult--prefix-group (cand transform) + "Return title for CAND or TRANSFORM the candidate. +The candidate must have a `consult--prefix-group' property." + (if transform + (substring cand (1+ (length (get-text-property 0 'consult--prefix-group cand)))) + (get-text-property 0 'consult--prefix-group cand))) + +(defun consult--type-group (types) + "Return group function for TYPES." + (lambda (cand transform) + (if transform cand + (alist-get (get-text-property 0 'consult--type cand) types)))) + +(defun consult--type-narrow (types) + "Return narrowing configuration from TYPES." + (list :predicate + (lambda (cand) (eq (get-text-property 0 'consult--type cand) consult--narrow)) + :keys types)) + +(defun consult--widen-key () + "Return widening key, if `consult-widen-key' is not set. +The default is twice the `consult-narrow-key'." + (cond + (consult-widen-key + (consult--key-parse consult-widen-key)) + (consult-narrow-key + (let ((key (consult--key-parse consult-narrow-key))) + (vconcat key key))))) + +(defun consult-narrow (key) + "Narrow current completion with KEY. + +This command is used internally by the narrowing system of `consult--read'." + (interactive + (list (unless (equal (this-single-command-keys) (consult--widen-key)) + last-command-event))) + (consult--require-minibuffer) + (setq consult--narrow key) + (when consult--narrow-predicate + (setq minibuffer-completion-predicate (and consult--narrow consult--narrow-predicate))) + (when consult--narrow-overlay + (delete-overlay consult--narrow-overlay)) + (when consult--narrow + (setq consult--narrow-overlay + (consult--make-overlay + (1- (minibuffer-prompt-end)) (minibuffer-prompt-end) + 'before-string + (propertize (format " [%s]" (alist-get consult--narrow + consult--narrow-keys)) + 'face 'consult-narrow-indicator)))) + (run-hooks 'consult--completion-refresh-hook)) + +(defconst consult--narrow-delete + `(menu-item + "" nil :filter + ,(lambda (&optional _) + (when (equal (minibuffer-contents-no-properties) "") + (lambda () + (interactive) + (consult-narrow nil)))))) + +(defconst consult--narrow-space + `(menu-item + "" nil :filter + ,(lambda (&optional _) + (let ((str (minibuffer-contents-no-properties))) + (when-let (pair (or (and (length= str 1) + (assoc (aref str 0) consult--narrow-keys)) + (and (equal str "") + (assoc ?\s consult--narrow-keys)))) + (lambda () + (interactive) + (delete-minibuffer-contents) + (consult-narrow (car pair)))))))) + +(defun consult-narrow-help () + "Print narrowing help as a `minibuffer-message'. + +This command can be bound to a key in `consult-narrow-map', +to make it available for commands with narrowing." + (interactive) + (consult--require-minibuffer) + (let ((minibuffer-message-timeout 1000000)) + (minibuffer-message + (mapconcat (lambda (x) + (concat + (propertize (key-description (list (car x))) 'face 'consult-key) + " " + (propertize (cdr x) 'face 'consult-help))) + consult--narrow-keys + " ")))) + +(defun consult--narrow-setup (settings map) + "Setup narrowing with SETTINGS and keymap MAP." + (if (memq :keys settings) + (setq consult--narrow-predicate (plist-get settings :predicate) + consult--narrow-keys (plist-get settings :keys)) + (setq consult--narrow-predicate nil + consult--narrow-keys settings)) + (when-let ((key consult-narrow-key)) + (setq key (consult--key-parse key)) + (dolist (pair consult--narrow-keys) + (define-key map (vconcat key (vector (car pair))) + (cons (cdr pair) #'consult-narrow)))) + (when-let ((widen (consult--widen-key))) + (define-key map widen (cons "All" #'consult-narrow))) + (when-let ((init (and (memq :keys settings) (plist-get settings :initial)))) + (consult-narrow init))) + +;; Emacs 28: hide in M-X +(put #'consult-narrow-help 'completion-predicate #'ignore) +(put #'consult-narrow 'completion-predicate #'ignore) + +;;;; Splitting completion style + +(defun consult--split-perl (str &optional _plist) + "Split input STR in async input and filtering part. + +The function returns a list with three elements: The async +string, the start position of the completion filter string and a +force flag. If the first character is a punctuation character it +determines the separator. Examples: \"/async/filter\", +\"#async#filter\"." + (if (string-match-p "^[[:punct:]]" str) + (save-match-data + (let ((q (regexp-quote (substring str 0 1)))) + (string-match (concat "^" q "\\([^" q "]*\\)\\(" q "\\)?") str) + `(,(match-string 1 str) + ,(match-end 0) + ;; Force update it two punctuation characters are entered. + ,(match-end 2) + ;; List of highlights + (0 . ,(match-beginning 1)) + ,@(and (match-end 2) `((,(match-beginning 2) . ,(match-end 2))))))) + `(,str ,(length str)))) + +(defun consult--split-nil (str &optional _plist) + "Treat the complete input STR as async input." + `(,str ,(length str))) + +(defun consult--split-separator (str plist) + "Split input STR in async input and filtering part at first separator. +PLIST is the splitter configuration, including the separator." + (let ((sep (regexp-quote (char-to-string (plist-get plist :separator))))) + (save-match-data + (if (string-match (format "^\\([^%s]+\\)\\(%s\\)?" sep sep) str) + `(,(match-string 1 str) + ,(match-end 0) + ;; Force update it space is entered. + ,(match-end 2) + ;; List of highlights + ,@(and (match-end 2) `((,(match-beginning 2) . ,(match-end 2))))) + `(,str ,(length str)))))) + +(defun consult--split-setup (split) + "Setup splitting completion style with splitter function SPLIT." + (let* ((styles completion-styles) + (catdef completion-category-defaults) + (catovr completion-category-overrides) + (try (lambda (str table pred point) + (let ((completion-styles styles) + (completion-category-defaults catdef) + (completion-category-overrides catovr) + (pos (cadr (funcall split str)))) + (pcase (completion-try-completion (substring str pos) table pred + (max 0 (- point pos))) + ('t t) + (`(,newstr . ,newpt) + (cons (concat (substring str 0 pos) newstr) + (+ pos newpt))))))) + (all (lambda (str table pred point) + (let ((completion-styles styles) + (completion-category-defaults catdef) + (completion-category-overrides catovr) + (pos (cadr (funcall split str)))) + (completion-all-completions (substring str pos) table pred + (max 0 (- point pos))))))) + (setq-local completion-styles-alist (cons `(consult--split ,try ,all "") + completion-styles-alist) + completion-styles '(consult--split) + completion-category-defaults nil + completion-category-overrides nil))) + +;;;; Asynchronous filtering functions + +(defun consult--async-p (fun) + "Return t if FUN is an asynchronous completion function." + (and (functionp fun) + (condition-case nil + (progn (funcall fun "" nil 'metadata) nil) + (wrong-number-of-arguments t)))) + +(defmacro consult--with-async (bind &rest body) + "Setup asynchronous completion in BODY. + +BIND is the asynchronous function binding." + (declare (indent 1)) + (let ((async (car bind))) + `(let ((,async ,@(cdr bind)) + (new-chunk (max read-process-output-max consult--process-chunk)) + orig-chunk) + (consult--minibuffer-with-setup-hook + ;; Append such that we overwrite the completion style setting of + ;; `fido-mode'. See `consult--async-split' and + ;; `consult--split-setup'. + (:append + (lambda () + (when (consult--async-p ,async) + (setq orig-chunk read-process-output-max + read-process-output-max new-chunk) + (funcall ,async 'setup) + (let* ((mb (current-buffer)) + (fun (lambda () + (when-let (win (active-minibuffer-window)) + (when (eq (window-buffer win) mb) + (with-current-buffer mb + (let ((inhibit-modification-hooks t)) + ;; Push input string to request refresh. + (funcall ,async (minibuffer-contents-no-properties)))))))) + ;; We use a symbol in order to avoid adding lambdas to + ;; the hook variable. Symbol indirection because of + ;; bug#46407. + (hook (make-symbol "consult--async-after-change-hook"))) + ;; Delay modification hook to ensure that minibuffer is still + ;; alive after the change, such that we don't restart a new + ;; asynchronous search right before exiting the minibuffer. + (fset hook (lambda (&rest _) (run-at-time 0 nil fun))) + (add-hook 'after-change-functions hook nil 'local) + (funcall hook))))) + (let ((,async (if (consult--async-p ,async) ,async (lambda (_) ,async)))) + (unwind-protect + ,(macroexp-progn body) + (funcall ,async 'destroy) + (when (and orig-chunk (eq read-process-output-max new-chunk)) + (setq read-process-output-max orig-chunk)))))))) + +(defun consult--async-sink () + "Create ASYNC sink function. + +An async function must accept a single action argument. For the +\\='setup action it is guaranteed that the call originates from +the minibuffer. For the other actions no assumption about the +context can be made. + +\\='setup Setup the internal closure state. Return nil. +\\='destroy Destroy the internal closure state. Return nil. +\\='flush Flush the list of candidates. Return nil. +\\='refresh Request UI refresh. Return nil. +nil Return the list of candidates. +list Append the list to the already existing candidates list and return it. +string Update with the current user input string. Return nil." + (let (candidates last buffer) + (lambda (action) + (pcase-exhaustive action + ('setup + (setq buffer (current-buffer)) + nil) + ((or (pred stringp) 'destroy) nil) + ('flush (setq candidates nil last nil)) + ('refresh + ;; Refresh the UI when the current minibuffer window belongs + ;; to the current asynchronous completion session. + (when-let (win (active-minibuffer-window)) + (when (eq (window-buffer win) buffer) + (with-selected-window win + (run-hooks 'consult--completion-refresh-hook) + ;; Interaction between asynchronous completion functions and + ;; preview: We have to trigger preview immediately when + ;; candidates arrive (gh:minad/consult#436). + (when (and consult--preview-function candidates) + (funcall consult--preview-function))))) + nil) + ('nil candidates) + ((pred consp) + (setq last (last (if last (setcdr last action) (setq candidates action)))) + candidates))))) + +(defun consult--async-split-style () + "Return the async splitting style function and initial string." + (or (alist-get consult-async-split-style consult-async-split-styles-alist) + (user-error "Splitting style `%s' not found" consult-async-split-style))) + +(defun consult--async-split-initial (initial) + "Return initial string for async command. +INITIAL is the additional initial string." + (concat (plist-get (consult--async-split-style) :initial) initial)) + +(defun consult--async-split-thingatpt (thing) + "Return THING at point with async initial prefix." + (when-let (str (thing-at-point thing)) + (consult--async-split-initial str))) + +(defun consult--async-split (async &optional split) + "Create async function, which splits the input string. +ASYNC is the async sink. +SPLIT is the splitting function." + (unless split + (let* ((style (consult--async-split-style)) + (fn (plist-get style :function))) + (setq split (lambda (str) (funcall fn str style))))) + (lambda (action) + (pcase action + ('setup + (consult--split-setup split) + (funcall async 'setup)) + ((pred stringp) + (pcase-let* ((`(,async-str ,_ ,force . ,highlights) + (funcall split action)) + (async-len (length async-str)) + (input-len (length action)) + (end (minibuffer-prompt-end))) + ;; Highlight punctuation characters + (remove-list-of-text-properties end (+ end input-len) '(face)) + (dolist (hl highlights) + (put-text-property (+ end (car hl)) (+ end (cdr hl)) + 'face 'consult-async-split)) + (funcall async + ;; Pass through if the input is long enough! + (if (or force (>= async-len consult-async-min-input)) + async-str + ;; Pretend that there is no input + "")))) + (_ (funcall async action))))) + +(defun consult--async-indicator (async) + "Create async function with a state indicator overlay. +ASYNC is the async sink." + (let (ov) + (lambda (action &optional state) + (pcase action + ('indicator + (overlay-put ov 'display + (pcase-exhaustive state + ('running #("*" 0 1 (face consult-async-running))) + ('finished #(":" 0 1 (face consult-async-finished))) + ('killed #(";" 0 1 (face consult-async-failed))) + ('failed #("!" 0 1 (face consult-async-failed)))))) + ('setup + (setq ov (make-overlay (- (minibuffer-prompt-end) 2) + (- (minibuffer-prompt-end) 1))) + (funcall async 'setup)) + ('destroy + (delete-overlay ov) + (funcall async 'destroy)) + (_ (funcall async action)))))) + +(defun consult--async-log (formatted &rest args) + "Log FORMATTED ARGS to variable `consult--async-log'." + (with-current-buffer (get-buffer-create consult--async-log) + (goto-char (point-max)) + (insert (apply #'format formatted args)))) + +(defun consult--async-process (async builder &rest props) + "Create process source async function. + +ASYNC is the async function which receives the candidates. +BUILDER is the command line builder function. +PROPS are optional properties passed to `make-process'." + (setq async (consult--async-indicator async)) + (let (proc proc-buf last-args count) + (lambda (action) + (pcase action + ("" ;; If no input is provided kill current process + (when proc + (delete-process proc) + (kill-buffer proc-buf) + (setq proc nil proc-buf nil)) + (setq last-args nil)) + ((pred stringp) + (funcall async action) + (let* ((args (funcall builder action))) + (unless (stringp (car args)) + (setq args (car args))) + (unless (equal args last-args) + (setq last-args args) + (when proc + (delete-process proc) + (kill-buffer proc-buf) + (setq proc nil proc-buf nil)) + (when args + (let* ((flush t) + (rest "") + (proc-filter + (lambda (_ out) + (when flush + (setq flush nil) + (funcall async 'flush)) + (let ((lines (split-string out "[\r\n]+"))) + (if (not (cdr lines)) + (setq rest (concat rest (car lines))) + (setcar lines (concat rest (car lines))) + (let* ((len (length lines)) + (last (nthcdr (- len 2) lines))) + (setq rest (cadr last) + count (+ count len -1)) + (setcdr last nil) + (funcall async lines)))))) + (proc-sentinel + (lambda (_ event) + (when flush + (setq flush nil) + (funcall async 'flush)) + (funcall async 'indicator + (cond + ((string-prefix-p "killed" event) 'killed) + ((string-prefix-p "finished" event) 'finished) + (t 'failed))) + (when (and (string-prefix-p "finished" event) (not (equal rest ""))) + (cl-incf count) + (funcall async (list rest))) + (consult--async-log + "consult--async-process sentinel: event=%s lines=%d\n" + (string-trim event) count) + (when (> (buffer-size proc-buf) 0) + (with-current-buffer (get-buffer-create consult--async-log) + (goto-char (point-max)) + (insert ">>>>> stderr >>>>>\n") + (let ((beg (point))) + (insert-buffer-substring proc-buf) + (save-excursion + (goto-char beg) + (message #("%s" 0 2 (face error)) + (buffer-substring-no-properties (pos-bol) (pos-eol))))) + (insert "<<<<< stderr <<<<<\n"))))) + (process-adaptive-read-buffering nil)) + (funcall async 'indicator 'running) + (consult--async-log "consult--async-process started: args=%S default-directory=%S\n" + args default-directory) + (setq count 0 + proc-buf (generate-new-buffer " *consult-async-stderr*") + proc (apply #'make-process + `(,@props + :connection-type pipe + :name ,(car args) + ;;; XXX tramp bug, the stderr buffer must be empty + :stderr ,proc-buf + :noquery t + :command ,args + :filter ,proc-filter + :sentinel ,proc-sentinel))))))) + nil) + ('destroy + (when proc + (delete-process proc) + (kill-buffer proc-buf) + (setq proc nil proc-buf nil)) + (funcall async 'destroy)) + (_ (funcall async action)))))) + +(defun consult--async-highlight (async builder) + "Return a new ASYNC function with candidate highlighting. +BUILDER is the command line builder function." + (let (highlight) + (lambda (action) + (cond + ((stringp action) + (setq highlight (cdr (funcall builder action))) + (funcall async action)) + ((and (consp action) highlight) + (dolist (str action) + (funcall highlight str)) + (funcall async action)) + (t (funcall async action)))))) + +(defun consult--async-throttle (async &optional throttle debounce) + "Create async function from ASYNC which throttles input. + +The THROTTLE delay defaults to `consult-async-input-throttle'. +The DEBOUNCE delay defaults to `consult-async-input-debounce'." + (setq throttle (or throttle consult-async-input-throttle) + debounce (or debounce consult-async-input-debounce)) + (let* ((input "") (timer (timer-create)) (last 0)) + (lambda (action) + (pcase action + ((pred stringp) + (unless (equal action input) + (cancel-timer timer) + (funcall async "") ;; cancel running process + (setq input action) + (unless (equal action "") + (timer-set-function timer (lambda () + (setq last (float-time)) + (funcall async action))) + (timer-set-time + timer + (timer-relative-time + nil (max debounce (- (+ last throttle) (float-time))))) + (timer-activate timer))) + nil) + ('destroy + (cancel-timer timer) + (funcall async 'destroy)) + (_ (funcall async action)))))) + +(defun consult--async-refresh-immediate (async) + "Create async function from ASYNC, which refreshes the display. + +The refresh happens immediately when candidates are pushed." + (lambda (action) + (pcase action + ((or (pred consp) 'flush) + (prog1 (funcall async action) + (funcall async 'refresh))) + (_ (funcall async action))))) + +(defun consult--async-refresh-timer (async &optional delay) + "Create async function from ASYNC, which refreshes the display. + +The refresh happens after a DELAY, defaulting to `consult-async-refresh-delay'." + (let ((delay (or delay consult-async-refresh-delay)) + (timer (timer-create))) + (timer-set-function timer async '(refresh)) + (lambda (action) + (prog1 (funcall async action) + (pcase action + ((or (pred consp) 'flush) + (unless (memq timer timer-list) + (timer-set-time timer (timer-relative-time nil delay)) + (timer-activate timer))) + ('destroy + (cancel-timer timer))))))) + +(defmacro consult--async-command (builder &rest args) + "Asynchronous command pipeline. +ARGS is a list of `make-process' properties and transforms. +BUILDER is the command line builder function, which takes the +input string and must either return a list of command line +arguments or a pair of the command line argument list and a +highlighting function." + (declare (indent 1)) + `(thread-first + (consult--async-sink) + (consult--async-refresh-timer) + ,@(seq-take-while (lambda (x) (not (keywordp x))) args) + (consult--async-process + ,builder + ,@(seq-drop-while (lambda (x) (not (keywordp x))) args)) + (consult--async-throttle) + (consult--async-split))) + +(defmacro consult--async-transform (async &rest transform) + "Use FUN to TRANSFORM candidates of ASYNC." + (cl-with-gensyms (async-var action-var) + `(let ((,async-var ,async)) + (lambda (,action-var) + (funcall ,async-var (if (consp ,action-var) (,@transform ,action-var) ,action-var)))))) + +(defun consult--async-map (async fun) + "Map candidates of ASYNC by FUN." + (consult--async-transform async mapcar fun)) + +(defun consult--async-filter (async fun) + "Filter candidates of ASYNC by FUN." + (consult--async-transform async seq-filter fun)) + +;;;; Dynamic collections based + +(defun consult--dynamic-compute (async fun &optional debounce) + "Dynamic computation of candidates. +ASYNC is the sink. +FUN computes the candidates given the input. +DEBOUNCE is the time after which an interrupted computation +should be restarted." + (setq debounce (or debounce consult-async-input-debounce)) + (setq async (consult--async-indicator async)) + (let* ((request) (current) (timer) + (cancel (lambda () (when timer (cancel-timer timer) (setq timer nil)))) + (start (lambda (req) (setq request req) (funcall async 'refresh)))) + (lambda (action) + (pcase action + ((and 'nil (guard (not request))) + (funcall async nil)) + ('nil + (funcall cancel) + (let ((state 'killed)) + (unwind-protect + (progn + (funcall async 'indicator 'running) + (redisplay) + ;; Run computation + (let ((response (funcall fun request))) + ;; Flush and update candidate list + (funcall async 'flush) + (setq state 'finished current request) + (funcall async response))) + (funcall async 'indicator state) + ;; If the computation was killed, restart it after some time. + (when (eq state 'killed) + (setq timer (run-at-time debounce nil start request))) + (setq request nil)))) + ((pred stringp) + (funcall cancel) + (if (or (equal action "") (equal action current)) + (funcall async 'indicator 'finished) + (funcall start action))) + ('destroy + (funcall cancel) + (funcall async 'destroy)) + (_ (funcall async action)))))) + +(defun consult--dynamic-collection (fun) + "Dynamic collection with input splitting. +FUN computes the candidates given the input." + (thread-first + (consult--async-sink) + (consult--dynamic-compute fun) + (consult--async-throttle) + (consult--async-split))) + +;;;; Special keymaps + +(defvar-keymap consult-async-map + :doc "Keymap added for commands with asynchronous candidates." + ;; Overwriting some unusable defaults of default minibuffer completion. + "<remap> <minibuffer-complete-word>" #'self-insert-command + ;; Remap Emacs 29 history and default completion for now + ;; (gh:minad/consult#613). + "<remap> <minibuffer-complete-defaults>" #'ignore + "<remap> <minibuffer-complete-history>" #'consult-history) + +(defvar-keymap consult-narrow-map + :doc "Narrowing keymap which is added to the local minibuffer map. +Note that `consult-narrow-key' and `consult-widen-key' are bound dynamically." + "SPC" consult--narrow-space + "DEL" consult--narrow-delete) + +;;;; Internal API: consult--read + +(defun consult--annotate-align (cand ann) + "Align annotation ANN by computing the maximum CAND width." + (setq consult--annotate-align-width + (max consult--annotate-align-width + (* (ceiling (consult--display-width cand) + consult--annotate-align-step) + consult--annotate-align-step))) + (when ann + (concat + #(" " 0 1 (display (space :align-to (+ left consult--annotate-align-width)))) + ann))) + +(defun consult--add-history (async items) + "Add ITEMS to the minibuffer future history. +ASYNC must be non-nil for async completion functions." + (delete-dups + (append + ;; the defaults are at the beginning of the future history + (ensure-list minibuffer-default) + ;; then our custom items + (remove "" (remq nil (ensure-list items))) + ;; Add all the completions for non-async commands. For async commands this + ;; feature is not useful, since if one selects a completion candidate, the + ;; async search is restarted using that candidate string. This usually does + ;; not yield a desired result since the async input uses a special format, + ;; e.g., `#grep#filter'. + (unless async + (all-completions "" + minibuffer-completion-table + minibuffer-completion-predicate))))) + +(defun consult--setup-keymap (keymap async narrow preview-key) + "Setup minibuffer keymap. + +KEYMAP is a command-specific keymap. +ASYNC must be non-nil for async completion functions. +NARROW are the narrow settings. +PREVIEW-KEY are the preview keys." + (let ((old-map (current-local-map)) + (map (make-sparse-keymap))) + + ;; Add narrow keys + (when narrow + (consult--narrow-setup narrow map)) + + ;; Preview trigger keys + (when (and (consp preview-key) (memq :keys preview-key)) + (setq preview-key (plist-get preview-key :keys))) + (setq preview-key (mapcar #'car (consult--preview-key-normalize preview-key))) + (when preview-key + (dolist (key preview-key) + (unless (or (eq key 'any) (lookup-key old-map key)) + (define-key map key #'ignore)))) + + ;; Put the keymap together + (use-local-map + (make-composed-keymap + (delq nil (list keymap + (and async consult-async-map) + (and narrow consult-narrow-map) + map)) + old-map)))) + +(defun consult--tofu-hide-in-minibuffer (&rest _) + "Hide the tofus in the minibuffer." + (let* ((min (minibuffer-prompt-end)) + (max (point-max)) + (pos max)) + (while (and (> pos min) (consult--tofu-p (char-before pos))) + (cl-decf pos)) + (when (< pos max) + (add-text-properties pos max '(invisible t rear-nonsticky t cursor-intangible t))))) + +(defun consult--read-annotate (fun cand) + "Annotate CAND with annotation function FUN." + (pcase (funcall fun cand) + (`(,_ ,_ ,suffix) suffix) + (ann ann))) + +(defun consult--read-affixate (fun cands) + "Affixate CANDS with annotation function FUN." + (mapcar (lambda (cand) + (let ((ann (funcall fun cand))) + (if (consp ann) + ann + (setq ann (or ann "")) + (list cand "" + ;; The default completion UI adds the + ;; `completions-annotations' face if no other faces are + ;; present. + (if (text-property-not-all 0 (length ann) 'face nil ann) + ann + (propertize ann 'face 'completions-annotations)))))) + cands)) + +(cl-defun consult--read-1 (table &key + prompt predicate require-match history default + keymap category initial narrow add-history annotate + state preview-key sort lookup group inherit-input-method) + "See `consult--read' for the documentation of the arguments." + (consult--minibuffer-with-setup-hook + (:append (lambda () + (add-hook 'after-change-functions #'consult--tofu-hide-in-minibuffer nil 'local) + (consult--setup-keymap keymap (consult--async-p table) narrow preview-key) + (setq-local minibuffer-default-add-function + (apply-partially #'consult--add-history (consult--async-p table) add-history)))) + (consult--with-async (async table) + (consult--with-preview + preview-key state + (lambda (narrow input cand) + (funcall lookup cand (funcall async nil) input narrow)) + (apply-partially #'run-hook-with-args-until-success + 'consult--completion-candidate-hook) + (pcase-exhaustive history + (`(:input ,var) var) + ((pred symbolp))) + ;; Do not unnecessarily let-bind the lambdas to avoid over-capturing in + ;; the interpreter. This will make closures and the lambda string + ;; representation larger, which makes debugging much worse. Fortunately + ;; the over-capturing problem does not affect the bytecode interpreter + ;; which does a proper scope analysis. + (let* ((metadata `(metadata + ,@(when category `((category . ,category))) + ,@(when group `((group-function . ,group))) + ,@(when annotate + `((affixation-function + . ,(apply-partially #'consult--read-affixate annotate)) + (annotation-function + . ,(apply-partially #'consult--read-annotate annotate)))) + ,@(unless sort '((cycle-sort-function . identity) + (display-sort-function . identity))))) + (consult--annotate-align-width 0) + (selected + (completing-read + prompt + (lambda (str pred action) + (let ((result (complete-with-action action (funcall async nil) str pred))) + (if (eq action 'metadata) + (if (and (eq (car result) 'metadata) (cdr result)) + ;; Merge metadata + `(metadata ,@(cdr metadata) ,@(cdr result)) + metadata) + result))) + predicate require-match initial + (if (symbolp history) history (cadr history)) + default + inherit-input-method))) + ;; Repair the null completion semantics. `completing-read' may return + ;; an empty string even if REQUIRE-MATCH is non-nil. One can always + ;; opt-in to null completion by passing the empty string for DEFAULT. + (when (and (eq require-match t) (not default) (equal selected "")) + (user-error "No selection")) + selected))))) + +(cl-defun consult--read (table &rest options &key + prompt predicate require-match history default + keymap category initial narrow add-history annotate + state preview-key sort lookup group inherit-input-method) + "Enhanced completing read function to select from TABLE. + +The function is a thin wrapper around `completing-read'. Keyword +arguments are used instead of positional arguments for code +clarity. On top of `completing-read' it additionally supports +computing the candidate list asynchronously, candidate preview +and narrowing. You should use `completing-read' instead of +`consult--read' if you don't use asynchronous candidate +computation or candidate preview. + +Keyword OPTIONS: + +PROMPT is the string which is shown as prompt in the minibuffer. +PREDICATE is a filter function called for each candidate, returns +nil or t. +REQUIRE-MATCH equals t means that an exact match is required. +HISTORY is the symbol of the history variable. +DEFAULT is the default selected value. +ADD-HISTORY is a list of items to add to the history. +CATEGORY is the completion category symbol. +SORT should be set to nil if the candidates are already sorted. +This will disable sorting in the completion UI. +LOOKUP is a lookup function passed the selected candidate string, +the list of candidates, the current input string and the current +narrowing value. +ANNOTATE is a function passed a candidate string. The function +should either return an annotation string or a list of three +strings (candidate prefix postfix). +INITIAL is the initial input string. +STATE is the state function, see `consult--with-preview'. +GROUP is a completion metadata `group-function' as documented in +the Elisp manual. +PREVIEW-KEY are the preview keys. Can be nil, `any', a single +key or a list of keys. +NARROW is an alist of narrowing prefix strings and description. +KEYMAP is a command-specific keymap. +INHERIT-INPUT-METHOD, if non-nil the minibuffer inherits the +input method." + ;; supported types + (cl-assert (or (functionp table) ;; dynamic table or asynchronous function + (obarrayp table) ;; obarray + (hash-table-p table) ;; hash table + (not table) ;; empty list + (stringp (car table)) ;; string list + (and (consp (car table)) (stringp (caar table))) ;; string alist + (and (consp (car table)) (symbolp (caar table))))) ;; symbol alist + (ignore prompt predicate require-match history default + keymap category initial narrow add-history annotate + state preview-key sort lookup group inherit-input-method) + (apply #'consult--read-1 table + (append + (consult--customize-get) + options + (list :prompt "Select: " + :preview-key consult-preview-key + :sort t + :lookup (lambda (selected &rest _) selected))))) + +;;;; Internal API: consult--prompt + +(cl-defun consult--prompt-1 (&key prompt history add-history initial default + keymap state preview-key transform inherit-input-method) + "See `consult--prompt' for documentation." + (consult--minibuffer-with-setup-hook + (:append (lambda () + (consult--setup-keymap keymap nil nil preview-key) + (setq-local minibuffer-default-add-function + (apply-partially #'consult--add-history nil add-history)))) + (consult--with-preview + preview-key state + (lambda (_narrow inp _cand) (funcall transform inp)) + (lambda () "") + history + (read-from-minibuffer prompt initial nil nil history default inherit-input-method)))) + +(cl-defun consult--prompt (&rest options &key prompt history add-history initial default + keymap state preview-key transform inherit-input-method) + "Read from minibuffer. + +Keyword OPTIONS: + +PROMPT is the string to prompt with. +TRANSFORM is a function which is applied to the current input string. +HISTORY is the symbol of the history variable. +INITIAL is initial input. +DEFAULT is the default selected value. +ADD-HISTORY is a list of items to add to the history. +STATE is the state function, see `consult--with-preview'. +PREVIEW-KEY are the preview keys (nil, `any', a single key or a list of keys). +KEYMAP is a command-specific keymap." + (ignore prompt history add-history initial default + keymap state preview-key transform inherit-input-method) + (apply #'consult--prompt-1 + (append + (consult--customize-get) + options + (list :prompt "Input: " + :preview-key consult-preview-key + :transform #'identity)))) + +;;;; Internal API: consult--multi + +(defsubst consult--multi-source (sources cand) + "Lookup source for CAND in SOURCES list." + (aref sources (consult--tofu-get cand))) + +(defun consult--multi-predicate (sources cand) + "Predicate function called for each candidate CAND given SOURCES." + (let* ((src (consult--multi-source sources cand)) + (narrow (plist-get src :narrow)) + (type (or (car-safe narrow) narrow -1))) + (or (eq consult--narrow type) + (not (or consult--narrow (plist-get src :hidden)))))) + +(defun consult--multi-narrow (sources) + "Return narrow list from SOURCES." + (thread-last sources + (mapcar (lambda (src) + (when-let (narrow (plist-get src :narrow)) + (if (consp narrow) + narrow + (when-let (name (plist-get src :name)) + (cons narrow name)))))) + (delq nil) + (delete-dups))) + +(defun consult--multi-annotate (sources cand) + "Annotate candidate CAND from multi SOURCES." + (consult--annotate-align + cand + (let ((src (consult--multi-source sources cand))) + (if-let ((fun (plist-get src :annotate))) + (funcall fun (cdr (get-text-property 0 'multi-category cand))) + (plist-get src :name))))) + +(defun consult--multi-group (sources cand transform) + "Return title of candidate CAND or TRANSFORM the candidate given SOURCES." + (if transform cand + (plist-get (consult--multi-source sources cand) :name))) + +(defun consult--multi-preview-key (sources) + "Return preview keys from SOURCES." + (list :predicate + (lambda (cand) + (if (plist-member (cdr cand) :preview-key) + (plist-get (cdr cand) :preview-key) + consult-preview-key)) + :keys + (delete-dups + (seq-mapcat (lambda (src) + (let ((key (if (plist-member src :preview-key) + (plist-get src :preview-key) + consult-preview-key))) + (ensure-list key))) + sources)))) + +(defun consult--multi-lookup (sources selected candidates _input narrow &rest _) + "Lookup SELECTED in CANDIDATES given SOURCES, with potential NARROW." + (if (or (string-blank-p selected) + (not (consult--tofu-p (aref selected (1- (length selected)))))) + ;; Non-existing candidate without Tofu or default submitted (empty string) + (let* ((src (cond + (narrow (seq-find (lambda (src) + (let ((n (plist-get src :narrow))) + (eq (or (car-safe n) n -1) narrow))) + sources)) + ((seq-find (lambda (src) (plist-get src :default)) sources)) + ((seq-find (lambda (src) (not (plist-get src :hidden))) sources)) + ((aref sources 0)))) + (idx (seq-position sources src)) + (def (and (string-blank-p selected) ;; default candidate + (seq-find (lambda (cand) (eq idx (consult--tofu-get cand))) candidates)))) + (if def + (cons (cdr (get-text-property 0 'multi-category def)) src) + `(,selected :match nil ,@src))) + (if-let (found (member selected candidates)) + ;; Existing candidate submitted + (cons (cdr (get-text-property 0 'multi-category (car found))) + (consult--multi-source sources selected)) + ;; Non-existing Tofu'ed candidate submitted, e.g., via Embark + `(,(substring selected 0 -1) :match nil ,@(consult--multi-source sources selected))))) + +(defun consult--multi-candidates (sources) + "Return `consult--multi' candidates from SOURCES." + (let ((idx 0) candidates) + (seq-doseq (src sources) + (let* ((face (and (plist-member src :face) `(face ,(plist-get src :face)))) + (cat (plist-get src :category)) + (items (plist-get src :items)) + (items (if (functionp items) (funcall items) items))) + (dolist (item items) + (let* ((str (or (car-safe item) item)) + (cand (consult--tofu-append str idx))) + ;; Preserve existing `multi-category' datum of the candidate. + (if (and (eq str item) (get-text-property 0 'multi-category str)) + (when face (add-text-properties 0 (length str) face cand)) + ;; Attach `multi-category' datum and face. + (add-text-properties + 0 (length str) + `(multi-category (,cat . ,(or (cdr-safe item) item)) ,@face) cand)) + (push cand candidates)))) + (cl-incf idx)) + (nreverse candidates))) + +(defun consult--multi-enabled-sources (sources) + "Return vector of enabled SOURCES." + (vconcat + (seq-filter (lambda (src) + (if-let (pred (plist-get src :enabled)) + (funcall pred) + t)) + (mapcar (lambda (src) + (if (symbolp src) (symbol-value src) src)) + sources)))) + +(defun consult--multi-state (sources) + "State function given SOURCES." + (when-let (states (delq nil (mapcar (lambda (src) + (when-let (fun (plist-get src :state)) + (cons src (funcall fun)))) + sources))) + (let (last-fun) + (pcase-lambda (action `(,cand . ,src)) + (pcase action + ('setup + (pcase-dolist (`(,_ . ,fun) states) + (funcall fun 'setup nil))) + ('exit + (pcase-dolist (`(,_ . ,fun) states) + (funcall fun 'exit nil))) + ('preview + (let ((selected-fun (cdr (assq src states)))) + ;; If the candidate source changed during preview communicate to + ;; the last source, that none of its candidates is previewed anymore. + (when (and last-fun (not (eq last-fun selected-fun))) + (funcall last-fun 'preview nil)) + (setq last-fun selected-fun) + (when selected-fun + (funcall selected-fun 'preview cand)))) + ('return + (let ((selected-fun (cdr (assq src states)))) + ;; Finish all the sources, except the selected one. + (pcase-dolist (`(,_ . ,fun) states) + (unless (eq fun selected-fun) + (funcall fun 'return nil))) + ;; Finish the source with the selected candidate + (when selected-fun + (funcall selected-fun 'return cand))))))))) + +(defun consult--multi (sources &rest options) + "Select from candidates taken from a list of SOURCES. + +OPTIONS is the plist of options passed to `consult--read'. The following +options are supported: :require-match, :history, :keymap, :initial, +:add-history, :sort and :inherit-input-method. The other options of +`consult--read' are used by the implementation of `consult--multi' and +should not be overwritten, except in in special scenarios. + +The function returns the selected candidate in the form (cons candidate +source-plist). The plist has the key :match with a value nil if the +candidate does not exist, t if the candidate exists and `new' if the +candidate has been created. The sources of the source list can either be +symbols of source variables or source values. Source values must be +plists with fields from the following list. + +Required source fields: +* :category - Completion category symbol. +* :items - List of strings to select from or function returning + list of strings. Note that the strings can use text properties + to carry metadata, which is then available to the :annotate, + :action and :state functions. + +Optional source fields: +* :name - Name of the source as a string, used for narrowing, + group titles and annotations. +* :narrow - Narrowing character or (character . string) pair. +* :enabled - Function which must return t if the source is enabled. +* :hidden - When t candidates of this source are hidden by default. +* :face - Face used for highlighting the candidates. +* :annotate - Annotation function called for each candidate, returns string. +* :history - Name of history variable to add selected candidate. +* :default - Must be t if the first item of the source is the default value. +* :action - Function called with the selected candidate. +* :new - Function called with new candidate name, only if :require-match is nil. +* :state - State constructor for the source, must return the + state function. The state function is informed about state + changes of the UI and can be used to implement preview. +* Other custom source fields can be added depending on the use + case. Note that the source is returned by `consult--multi' + together with the selected candidate." + (let* ((sources (consult--multi-enabled-sources sources)) + (candidates (consult--with-increased-gc + (consult--multi-candidates sources))) + (selected + (apply #'consult--read + candidates + (append + options + (list + :category 'multi-category + :predicate (apply-partially #'consult--multi-predicate sources) + :annotate (apply-partially #'consult--multi-annotate sources) + :group (apply-partially #'consult--multi-group sources) + :lookup (apply-partially #'consult--multi-lookup sources) + :preview-key (consult--multi-preview-key sources) + :narrow (consult--multi-narrow sources) + :state (consult--multi-state sources)))))) + (when-let (history (plist-get (cdr selected) :history)) + (add-to-history history (car selected))) + (if (plist-member (cdr selected) :match) + (when-let (fun (plist-get (cdr selected) :new)) + (funcall fun (car selected)) + (plist-put (cdr selected) :match 'new)) + (when-let (fun (plist-get (cdr selected) :action)) + (funcall fun (car selected))) + (setq selected `(,(car selected) :match t ,@(cdr selected)))) + selected)) + +;;;; Customization macro + +(defun consult--customize-put (cmds prop form) + "Set property PROP to FORM of commands CMDS." + (dolist (cmd cmds) + (cond + ((and (boundp cmd) (consp (symbol-value cmd))) + (setf (plist-get (symbol-value cmd) prop) (eval form 'lexical))) + ((functionp cmd) + (setf (plist-get (alist-get cmd consult--customize-alist) prop) form)) + (t (user-error "%s is neither a Command command nor a source" cmd)))) + nil) + +(defmacro consult-customize (&rest args) + "Set properties of commands or sources. +ARGS is a list of commands or sources followed by the list of +keyword-value pairs. For `consult-customize' to succeed, the +customized sources and commands must exist. When a command is +invoked, the value of `this-command' is used to lookup the +corresponding customization options." + (let (setter) + (while args + (let ((cmds (seq-take-while (lambda (x) (not (keywordp x))) args))) + (setq args (seq-drop-while (lambda (x) (not (keywordp x))) args)) + (while (keywordp (car args)) + (push `(consult--customize-put ',cmds ,(car args) ',(cadr args)) setter) + (setq args (cddr args))))) + (macroexp-progn setter))) + +(defun consult--customize-get () + "Get configuration from `consult--customize-alist' for `this-command'." + (mapcar (lambda (x) (eval x 'lexical)) + (alist-get this-command consult--customize-alist))) + +;;;; Commands + +;;;;; Command: consult-completion-in-region + +(defun consult--insertion-preview (start end) + "State function for previewing a candidate in a specific region. +The candidates are previewed in the region from START to END. This function is +used as the `:state' argument for `consult--read' in the `consult-yank' family +of functions and in `consult-completion-in-region'." + (unless (or (minibufferp) + ;; Disable preview if anything odd is going on with the markers. + ;; Otherwise we get "Marker points into wrong buffer errors". See + ;; gh:minad/consult#375, where Org mode source blocks are + ;; completed in a different buffer than the original buffer. This + ;; completion is probably also problematic in my Corfu completion + ;; package. + (not (eq (window-buffer) (current-buffer))) + (and (markerp start) (not (eq (marker-buffer start) (current-buffer)))) + (and (markerp end) (not (eq (marker-buffer end) (current-buffer))))) + (let (ov) + (lambda (action cand) + (cond + ((and (not cand) ov) + (delete-overlay ov) + (setq ov nil)) + ((and (eq action 'preview) cand) + (unless ov + (setq ov (consult--make-overlay start end + 'invisible t + 'window (selected-window)))) + ;; Use `add-face-text-property' on a copy of "cand in order to merge face properties + (setq cand (copy-sequence cand)) + (add-face-text-property 0 (length cand) 'consult-preview-insertion t cand) + ;; Use the `before-string' property since the overlay might be empty. + (overlay-put ov 'before-string cand))))))) + +;;;###autoload +(defun consult-completion-in-region (start end collection &optional predicate) + "Use minibuffer completion as the UI for `completion-at-point'. + +The function is called with 4 arguments: START END COLLECTION +PREDICATE. The arguments and expected return value are as +specified for `completion-in-region'. Use this function as a +value for `completion-in-region-function'." + (barf-if-buffer-read-only) + (let* ((initial (buffer-substring-no-properties start end)) + (metadata (completion-metadata initial collection predicate)) + ;; TODO: `minibuffer-completing-file-name' is mostly deprecated, but + ;; still in use. Packages should instead use the completion metadata. + (minibuffer-completing-file-name + (eq 'file (completion-metadata-get metadata 'category))) + (threshold (completion--cycle-threshold metadata)) + (all (completion-all-completions initial collection predicate (length initial))) + ;; Wrap all annotation functions to ensure that they are executed + ;; in the original buffer. + (exit-fun (plist-get completion-extra-properties :exit-function)) + (ann-fun (plist-get completion-extra-properties :annotation-function)) + (aff-fun (plist-get completion-extra-properties :affixation-function)) + (docsig-fun (plist-get completion-extra-properties :company-docsig)) + (completion-extra-properties + `(,@(and ann-fun (list :annotation-function (consult--in-buffer ann-fun))) + ,@(and aff-fun (list :affixation-function (consult--in-buffer aff-fun))) + ;; Provide `:annotation-function' if `:company-docsig' is specified. + ,@(and docsig-fun (not ann-fun) (not aff-fun) + (list :annotation-function + (consult--in-buffer + (lambda (cand) + (concat (propertize " " 'display '(space :align-to center)) + (funcall docsig-fun cand))))))))) + ;; error if `threshold' is t or the improper list `all' is too short + (if (and threshold + (or (not (consp (ignore-errors (nthcdr threshold all)))) + (and completion-cycling completion-all-sorted-completions))) + (completion--in-region start end collection predicate) + (let* ((limit (car (completion-boundaries initial collection predicate ""))) + (this-command #'consult-completion-in-region) + (completion + (cond + ((atom all) nil) + ((and (consp all) (atom (cdr all))) + (concat (substring initial 0 limit) (car all))) + (t + (consult--local-let ((enable-recursive-minibuffers t)) + ;; Evaluate completion table in the original buffer. + ;; This is a reasonable thing to do and required by + ;; some completion tables in particular by lsp-mode. + ;; See gh:minad/vertico#61. + (consult--read (consult--completion-table-in-buffer collection) + :prompt "Completion: " + :state (consult--insertion-preview start end) + :predicate predicate + :initial initial)))))) + (if completion + (progn + ;; bug#55205: completion--replace removes properties! + (completion--replace start end (setq completion (concat completion))) + (when exit-fun + (funcall exit-fun completion + ;; If completion is finished and cannot be further + ;; completed, return `finished'. Otherwise return + ;; `exact'. + (if (eq (try-completion completion collection predicate) t) + 'finished 'exact))) + t) + (message "No completion") + nil))))) + +;;;;; Command: consult-outline + +(defun consult--outline-candidates () + "Return alist of outline headings and positions." + (consult--forbid-minibuffer) + (let* ((line (line-number-at-pos (point-min) consult-line-numbers-widen)) + (heading-regexp (concat "^\\(?:" + ;; default definition from outline.el + (or (bound-and-true-p outline-regexp) "[*\^L]+") + "\\)")) + (heading-alist (bound-and-true-p outline-heading-alist)) + (level-fun (or (bound-and-true-p outline-level) + (lambda () ;; as in the default from outline.el + (or (cdr (assoc (match-string 0) heading-alist)) + (- (match-end 0) (match-beginning 0)))))) + (buffer (current-buffer)) + candidates) + (save-excursion + (goto-char (point-min)) + (while (save-excursion + (if-let (fun (bound-and-true-p outline-search-function)) + (funcall fun) + (re-search-forward heading-regexp nil t))) + (cl-incf line (consult--count-lines (match-beginning 0))) + (push (consult--location-candidate + (consult--buffer-substring (pos-bol) (pos-eol) 'fontify) + (cons buffer (point)) (1- line) (1- line) + 'consult--outline-level (funcall level-fun)) + candidates) + (goto-char (1+ (pos-eol))))) + (unless candidates + (user-error "No headings")) + (nreverse candidates))) + +;;;###autoload +(defun consult-outline (&optional level) + "Jump to an outline heading, obtained by matching against `outline-regexp'. + +This command supports narrowing to a heading level and candidate +preview. The initial narrowing LEVEL can be given as prefix +argument. The symbol at point is added to the future history." + (interactive + (list (and current-prefix-arg (prefix-numeric-value current-prefix-arg)))) + (let* ((candidates (consult--slow-operation + "Collecting headings..." + (consult--outline-candidates))) + (min-level (- (cl-loop for cand in candidates minimize + (get-text-property 0 'consult--outline-level cand)) + ?1)) + (narrow-pred (lambda (cand) + (<= (get-text-property 0 'consult--outline-level cand) + (+ consult--narrow min-level)))) + (narrow-keys (mapcar (lambda (c) (cons c (format "Level %c" c))) + (number-sequence ?1 ?9))) + (narrow-init (and level (max ?1 (min ?9 (+ level ?0)))))) + (consult--read + candidates + :prompt "Go to heading: " + :annotate (consult--line-prefix) + :category 'consult-location + :sort nil + :require-match t + :lookup #'consult--line-match + :narrow `(:predicate ,narrow-pred :keys ,narrow-keys :initial ,narrow-init) + :history '(:input consult--line-history) + :add-history (thing-at-point 'symbol) + :state (consult--location-state candidates)))) + +;;;;; Command: consult-mark + +(defun consult--mark-candidates (markers) + "Return list of candidates strings for MARKERS." + (consult--forbid-minibuffer) + (let ((candidates) + (current-buf (current-buffer))) + (save-excursion + (dolist (marker markers) + (when-let ((pos (marker-position marker)) + (buf (marker-buffer marker))) + (when (and (eq buf current-buf) + (consult--in-range-p pos)) + (goto-char pos) + ;; `line-number-at-pos' is a very slow function, which should be + ;; replaced everywhere. However in this case the slow + ;; line-number-at-pos does not hurt much, since the mark ring is + ;; usually small since it is limited by `mark-ring-max'. + (push (consult--location-candidate + (consult--line-with-mark marker) marker + (line-number-at-pos pos consult-line-numbers-widen) + marker) + candidates))))) + (unless candidates + (user-error "No marks")) + (nreverse (delete-dups candidates)))) + +;;;###autoload +(defun consult-mark (&optional markers) + "Jump to a marker in MARKERS list (defaults to buffer-local `mark-ring'). + +The command supports preview of the currently selected marker position. +The symbol at point is added to the future history." + (interactive) + (consult--read + (consult--mark-candidates + (or markers (cons (mark-marker) mark-ring))) + :prompt "Go to mark: " + :annotate (consult--line-prefix) + :category 'consult-location + :sort nil + :require-match t + :lookup #'consult--lookup-location + :history '(:input consult--line-history) + :add-history (thing-at-point 'symbol) + :state (consult--jump-state))) + +;;;;; Command: consult-global-mark + +(defun consult--global-mark-candidates (markers) + "Return list of candidates strings for MARKERS." + (consult--forbid-minibuffer) + (let ((candidates)) + (save-excursion + (dolist (marker markers) + (when-let ((pos (marker-position marker)) + (buf (marker-buffer marker))) + (unless (minibufferp buf) + (with-current-buffer buf + (when (consult--in-range-p pos) + (goto-char pos) + ;; `line-number-at-pos' is slow, see comment in `consult--mark-candidates'. + (let* ((line (line-number-at-pos pos consult-line-numbers-widen)) + (prefix (consult--format-file-line-match (buffer-name buf) line "")) + (cand (concat prefix (consult--line-with-mark marker) (consult--tofu-encode marker)))) + (put-text-property 0 (length prefix) 'consult-strip t cand) + (put-text-property 0 (length cand) 'consult-location (cons marker line) cand) + (push cand candidates)))))))) + (unless candidates + (user-error "No global marks")) + (nreverse (delete-dups candidates)))) + +;;;###autoload +(defun consult-global-mark (&optional markers) + "Jump to a marker in MARKERS list (defaults to `global-mark-ring'). + +The command supports preview of the currently selected marker position. +The symbol at point is added to the future history." + (interactive) + (consult--read + (consult--global-mark-candidates + (or markers global-mark-ring)) + :prompt "Go to global mark: " + ;; Despite `consult-global-mark' formatting the candidates in grep-like + ;; style, we are not using the `consult-grep' category, since the candidates + ;; have location markers attached. + :category 'consult-location + :sort nil + :require-match t + :lookup #'consult--lookup-location + :history '(:input consult--line-history) + :add-history (thing-at-point 'symbol) + :state (consult--jump-state))) + +;;;;; Command: consult-line + +(defun consult--line-candidates (top curr-line) + "Return list of line candidates. +Start from top if TOP non-nil. +CURR-LINE is the current line number." + (consult--forbid-minibuffer) + (consult--fontify-all) + (let* ((buffer (current-buffer)) + (line (line-number-at-pos (point-min) consult-line-numbers-widen)) + default-cand candidates) + (consult--each-line beg end + (unless (looking-at-p "^\\s-*$") + (push (consult--location-candidate + (consult--buffer-substring beg end) + (cons buffer beg) line line) + candidates) + (when (and (not default-cand) (>= line curr-line)) + (setq default-cand candidates))) + (cl-incf line)) + (unless candidates + (user-error "No lines")) + (nreverse + (if (or top (not default-cand)) + candidates + (let ((before (cdr default-cand))) + (setcdr default-cand nil) + (nconc before candidates)))))) + +(defun consult--line-point-placement (selected candidates highlighted &rest ignored-faces) + "Find point position on matching line. +SELECTED is the currently selected candidate. +CANDIDATES is the list of candidates. +HIGHLIGHTED is the highlighted string to determine the match position. +IGNORED-FACES are ignored when determining the match position." + (when-let (pos (consult--lookup-location selected candidates)) + (if highlighted + (let* ((matches (apply #'consult--point-placement highlighted 0 ignored-faces)) + (dest (+ pos (car matches)))) + ;; Only create a new marker when jumping across buffers (for example + ;; `consult-line-multi'). Avoid creating unnecessary markers, when + ;; scrolling through candidates, since creating markers is not free. + (when (and (markerp pos) (not (eq (marker-buffer pos) (current-buffer)))) + (setq dest (move-marker (make-marker) dest (marker-buffer pos)))) + (cons dest (cdr matches))) + pos))) + +(defun consult--line-match (selected candidates input &rest _) + "Lookup position of match. +SELECTED is the currently selected candidate. +CANDIDATES is the list of candidates. +INPUT is the input string entered by the user." + (consult--line-point-placement selected candidates + (and (not (string-blank-p input)) + (car (consult--completion-filter + input + (list (substring-no-properties selected)) + 'consult-location 'highlight))) + 'completions-first-difference)) + +;;;###autoload +(defun consult-line (&optional initial start) + "Search for a matching line. + +Depending on the setting `consult-point-placement' the command +jumps to the beginning or the end of the first match on the line +or the line beginning. The default candidate is the non-empty +line next to point. This command obeys narrowing. Optional +INITIAL input can be provided. The search starting point is +changed if the START prefix argument is set. The symbol at point +and the last `isearch-string' is added to the future history." + (interactive (list nil (not (not current-prefix-arg)))) + (let* ((curr-line (line-number-at-pos (point) consult-line-numbers-widen)) + (top (not (eq start consult-line-start-from-top))) + (candidates (consult--slow-operation "Collecting lines..." + (consult--line-candidates top curr-line)))) + (consult--read + candidates + :prompt (if top "Go to line from top: " "Go to line: ") + :annotate (consult--line-prefix curr-line) + :category 'consult-location + :sort nil + :require-match t + ;; Always add last `isearch-string' to future history + :add-history (list (thing-at-point 'symbol) isearch-string) + :history '(:input consult--line-history) + :lookup #'consult--line-match + :default (car candidates) + ;; Add `isearch-string' as initial input if starting from Isearch + :initial (or initial + (and isearch-mode + (prog1 isearch-string (isearch-done)))) + :state (consult--location-state candidates)))) + +;;;;; Command: consult-line-multi + +(defun consult--line-multi-match (selected candidates &rest _) + "Lookup position of match. +SELECTED is the currently selected candidate. +CANDIDATES is the list of candidates." + (consult--line-point-placement selected candidates + (car (member selected candidates)))) + +(defun consult--line-multi-group (cand transform) + "Group function used by `consult-line-multi'. +If TRANSFORM non-nil, return transformed CAND, otherwise return title." + (if transform cand + (let* ((marker (car (get-text-property 0 'consult-location cand))) + (buf (if (consp marker) + (car marker) ;; Handle cheap marker + (marker-buffer marker)))) + (if buf (buffer-name buf) "Dead buffer")))) + +(defun consult--line-multi-candidates (buffers input) + "Collect matching candidates from multiple buffers. +INPUT is the user input which should be matched. +BUFFERS is the list of buffers." + (pcase-let ((`(,regexps . ,hl) + (funcall consult--regexp-compiler + input 'emacs completion-ignore-case)) + (candidates nil) + (cand-idx 0)) + (save-match-data + (dolist (buf buffers (nreverse candidates)) + (with-current-buffer buf + (save-excursion + (let ((line (line-number-at-pos (point-min) consult-line-numbers-widen))) + (goto-char (point-min)) + (while (and (not (eobp)) + (save-excursion (re-search-forward (car regexps) nil t))) + (cl-incf line (consult--count-lines (match-beginning 0))) + (let ((bol (pos-bol)) + (eol (pos-eol))) + (goto-char bol) + (when (and (not (looking-at-p "^\\s-*$")) + (seq-every-p (lambda (r) + (goto-char bol) + (re-search-forward r eol t)) + (cdr regexps))) + (push (consult--location-candidate + (funcall hl (buffer-substring-no-properties bol eol)) + (cons buf bol) (1- line) cand-idx) + candidates) + (cl-incf cand-idx)) + (goto-char (1+ eol))))))))))) + +;;;###autoload +(defun consult-line-multi (query &optional initial) + "Search for a matching line in multiple buffers. + +By default search across all project buffers. If the prefix +argument QUERY is non-nil, all buffers are searched. Optional +INITIAL input can be provided. The symbol at point and the last +`isearch-string' is added to the future history. In order to +search a subset of buffers, QUERY can be set to a plist according +to `consult--buffer-query'." + (interactive "P") + (unless (keywordp (car-safe query)) + (setq query (list :sort 'alpha-current :directory (and (not query) 'project)))) + (pcase-let* ((`(,prompt . ,buffers) (consult--buffer-query-prompt "Go to line" query)) + (collection (consult--dynamic-collection + (apply-partially #'consult--line-multi-candidates + buffers)))) + (consult--read + collection + :prompt prompt + :annotate (consult--line-prefix) + :category 'consult-location + :sort nil + :require-match t + ;; Always add last Isearch string to future history + :add-history (mapcar #'consult--async-split-initial + (delq nil (list (thing-at-point 'symbol) + isearch-string))) + :history '(:input consult--line-multi-history) + :lookup #'consult--line-multi-match + ;; Add `isearch-string' as initial input if starting from Isearch + :initial (consult--async-split-initial + (or initial + (and isearch-mode + (prog1 isearch-string (isearch-done))))) + :state (consult--location-state (lambda () (funcall collection nil))) + :group #'consult--line-multi-group))) + +;;;;; Command: consult-keep-lines + +(defun consult--keep-lines-state (filter) + "State function for `consult-keep-lines' with FILTER function." + (let ((font-lock-orig font-lock-mode) + (whitespace-orig (bound-and-true-p whitespace-mode)) + (hl-line-orig (bound-and-true-p hl-line-mode)) + (point-orig (point)) + lines content-orig replace last-input) + (if (use-region-p) + (save-restriction + ;; Use the same behavior as `keep-lines'. + (let ((rbeg (region-beginning)) + (rend (save-excursion + (goto-char (region-end)) + (unless (or (bolp) (eobp)) + (forward-line 0)) + (point)))) + (consult--fontify-region rbeg rend) + (narrow-to-region rbeg rend) + (consult--each-line beg end + (push (consult--buffer-substring beg end) lines)) + (setq content-orig (buffer-string) + replace (lambda (content &optional pos) + (delete-region rbeg rend) + (insert-before-markers content) + (goto-char (or pos rbeg)) + (setq rend (+ rbeg (length content))) + (add-face-text-property rbeg rend 'region t))))) + (consult--fontify-all) + (setq content-orig (buffer-string) + replace (lambda (content &optional pos) + (delete-region (point-min) (point-max)) + (insert content) + (goto-char (or pos (point-min))))) + (consult--each-line beg end + (push (consult--buffer-substring beg end) lines))) + (setq lines (nreverse lines)) + (lambda (action input) + ;; Restoring content and point position + (when (and (eq action 'return) last-input) + ;; No undo recording, modification hooks, buffer modified-status + (with-silent-modifications (funcall replace content-orig point-orig))) + ;; Committing or new input provided -> Update + (when (and input ;; Input has been provided + (or + ;; Committing, but not with empty input + (and (eq action 'return) (not (string-match-p "\\`!? ?\\'" input))) + ;; Input has changed + (not (equal input last-input)))) + (let ((filtered-content + (if (string-match-p "\\`!? ?\\'" input) + ;; Special case the empty input for performance. + ;; Otherwise it could happen that the minibuffer is empty, + ;; but the buffer has not been updated. + content-orig + (if (eq action 'return) + (apply #'concat (mapcan (lambda (x) (list x "\n")) + (funcall filter input lines))) + (while-no-input + ;; Heavy computation is interruptible if *not* committing! + ;; Allocate new string candidates since the matching function mutates! + (apply #'concat (mapcan (lambda (x) (list x "\n")) + (funcall filter input (mapcar #'copy-sequence lines))))))))) + (when (stringp filtered-content) + (when font-lock-mode (font-lock-mode -1)) + (when (bound-and-true-p whitespace-mode) (whitespace-mode -1)) + (when (bound-and-true-p hl-line-mode) (hl-line-mode -1)) + (if (eq action 'return) + (atomic-change-group + ;; Disable modification hooks for performance + (let ((inhibit-modification-hooks t)) + (funcall replace filtered-content))) + ;; No undo recording, modification hooks, buffer modified-status + (with-silent-modifications + (funcall replace filtered-content) + (setq last-input input)))))) + ;; Restore modes + (when (eq action 'return) + (when hl-line-orig (hl-line-mode 1)) + (when whitespace-orig (whitespace-mode 1)) + (when font-lock-orig (font-lock-mode 1)))))) + +;;;###autoload +(defun consult-keep-lines (filter &optional initial) + "Select a subset of the lines in the current buffer with live preview. + +The selected lines are kept and the other lines are deleted. When called +interactively, the lines selected are those that match the minibuffer input. In +order to match the inverse of the input, prefix the input with `! '. When +called from Elisp, the filtering is performed by a FILTER function. This +command obeys narrowing. + +FILTER is the filter function. +INITIAL is the initial input." + (interactive + (list (lambda (pattern cands) + ;; Use consult-location completion category when filtering lines + (consult--completion-filter-dispatch + pattern cands 'consult-location 'highlight)))) + (consult--forbid-minibuffer) + (let ((ro buffer-read-only)) + (unwind-protect + (consult--minibuffer-with-setup-hook + (lambda () + (when ro + (minibuffer-message + (substitute-command-keys + " [Unlocked read-only buffer. \\[minibuffer-keyboard-quit] to quit.]")))) + (setq buffer-read-only nil) + (consult--with-increased-gc + (consult--prompt + :prompt "Keep lines: " + :initial initial + :history 'consult--line-history + :state (consult--keep-lines-state filter)))) + (setq buffer-read-only ro)))) + +;;;;; Command: consult-focus-lines + +(defun consult--focus-lines-state (filter) + "State function for `consult-focus-lines' with FILTER function." + (let (lines overlays last-input pt-orig pt-min pt-max) + (save-excursion + (save-restriction + (if (not (use-region-p)) + (consult--fontify-all) + (consult--fontify-region (region-beginning) (region-end)) + (narrow-to-region + (region-beginning) + ;; Behave the same as `keep-lines'. + ;; Move to the next line. + (save-excursion + (goto-char (region-end)) + (unless (or (bolp) (eobp)) + (forward-line 0)) + (point)))) + (setq pt-orig (point) pt-min (point-min) pt-max (point-max)) + (let ((i 0)) + (consult--each-line beg end + ;; Use "\n" for empty lines, since we need a non-empty string to + ;; attach the text property to. + (let ((line (if (eq beg end) (char-to-string ?\n) + (buffer-substring-no-properties beg end)))) + (put-text-property 0 1 'consult--focus-line (cons (cl-incf i) beg) line) + (push line lines))) + (setq lines (nreverse lines))))) + (lambda (action input) + ;; New input provided -> Update + (when (and input (not (equal input last-input))) + (let (new-overlays) + (pcase (while-no-input + (unless (string-match-p "\\`!? ?\\'" input) ;; Empty input. + (let* ((inhibit-quit (eq action 'return)) ;; Non interruptible, when quitting! + (not (string-prefix-p "! " input)) + (stripped (string-remove-prefix "! " input)) + (matches (funcall filter stripped lines)) + (old-ind 0) + (block-beg pt-min) + (block-end pt-min)) + (while old-ind + (let ((match (pop matches)) (ind nil) (beg pt-max) (end pt-max) prop) + (when match + (setq prop (get-text-property 0 'consult--focus-line match) + ind (car prop) + beg (cdr prop) + ;; Check for empty lines, see above. + end (+ 1 beg (if (equal match "\n") 0 (length match))))) + (unless (eq ind (1+ old-ind)) + (let ((a (if not block-beg block-end)) + (b (if not block-end beg))) + (when (/= a b) + (push (consult--make-overlay a b 'invisible t) new-overlays))) + (setq block-beg beg)) + (setq block-end end old-ind ind))))) + 'commit) + ('commit + (mapc #'delete-overlay overlays) + (setq last-input input overlays new-overlays)) + (_ (mapc #'delete-overlay new-overlays))))) + (when (eq action 'return) + (cond + ((not input) + (mapc #'delete-overlay overlays) + (goto-char pt-orig)) + ((equal input "") + (consult-focus-lines nil 'show) + (goto-char pt-orig)) + (t + ;; Successfully terminated -> Remember invisible overlays + (setq consult--focus-lines-overlays + (nconc consult--focus-lines-overlays overlays)) + ;; move point past invisible + (goto-char (if-let (ov (and (invisible-p pt-orig) + (seq-find (lambda (ov) (overlay-get ov 'invisible)) + (overlays-at pt-orig)))) + (overlay-end ov) + pt-orig)))))))) + +;;;###autoload +(defun consult-focus-lines (filter &optional show initial) + "Hide or show lines using overlays. + +The selected lines are shown and the other lines hidden. When called +interactively, the lines selected are those that match the minibuffer input. In +order to match the inverse of the input, prefix the input with `! '. With +optional prefix argument SHOW reveal the hidden lines. Alternatively the +command can be restarted to reveal the lines. When called from Elisp, the +filtering is performed by a FILTER function. This command obeys narrowing. + +FILTER is the filter function. +INITIAL is the initial input." + (interactive + (list (lambda (pattern cands) + ;; Use consult-location completion category when filtering lines + (consult--completion-filter-dispatch + pattern cands 'consult-location nil)) + current-prefix-arg)) + (if show + (progn + (mapc #'delete-overlay consult--focus-lines-overlays) + (setq consult--focus-lines-overlays nil) + (message "All lines revealed")) + (consult--forbid-minibuffer) + (consult--with-increased-gc + (consult--prompt + :prompt + (if consult--focus-lines-overlays + "Focus on lines (RET to reveal): " + "Focus on lines: ") + :initial initial + :history 'consult--line-history + :state (consult--focus-lines-state filter))))) + +;;;;; Command: consult-goto-line + +(defun consult--goto-line-position (str msg) + "Transform input STR to line number. +Print an error message with MSG function." + (save-match-data + (if (and str (string-match "\\`\\([[:digit:]]+\\):?\\([[:digit:]]*\\)\\'" str)) + (let ((line (string-to-number (match-string 1 str))) + (col (string-to-number (match-string 2 str)))) + (save-excursion + (save-restriction + (when consult-line-numbers-widen + (widen)) + (goto-char (point-min)) + (forward-line (1- line)) + (goto-char (min (+ (point) col) (pos-eol))) + (point)))) + (when (and str (not (equal str ""))) + (funcall msg "Please enter a number.")) + nil))) + +;;;###autoload +(defun consult-goto-line (&optional arg) + "Read line number and jump to the line with preview. + +Enter either a line number to jump to the first column of the +given line or line:column in order to jump to a specific column. +Jump directly if a line number is given as prefix ARG. The +command respects narrowing and the settings +`consult-goto-line-numbers' and `consult-line-numbers-widen'." + (interactive "P") + (if arg + (call-interactively #'goto-line) + (consult--forbid-minibuffer) + (consult--local-let ((display-line-numbers consult-goto-line-numbers) + (display-line-numbers-widen consult-line-numbers-widen)) + (while (if-let (pos (consult--goto-line-position + (consult--prompt + :prompt "Go to line: " + :history 'goto-line-history + :state + (let ((preview (consult--jump-preview))) + (lambda (action str) + (funcall preview action + (consult--goto-line-position str #'ignore))))) + #'minibuffer-message)) + (consult--jump pos) + t))))) + +;;;;; Command: consult-recent-file + +(defun consult--file-preview () + "Create preview function for files." + (let ((open (consult--temporary-files)) + (preview (consult--buffer-preview))) + (lambda (action cand) + (unless cand + (funcall open)) + (funcall preview action + (and cand + (eq action 'preview) + (funcall open cand)))))) + +(defun consult--file-action (file) + "Open FILE via `consult--buffer-action'." + ;; Try to preserve the buffer as is, if it has already been opened, for + ;; example in literal or raw mode. + (setq file (abbreviate-file-name (expand-file-name file))) + (consult--buffer-action (or (get-file-buffer file) (find-file-noselect file)))) + +(consult--define-state file) + +;;;###autoload +(defun consult-recent-file () + "Find recent file using `completing-read'." + (interactive) + (find-file + (consult--read + (or + (mapcar #'consult--fast-abbreviate-file-name (bound-and-true-p recentf-list)) + (user-error "No recent files, `recentf-mode' is %s" + (if recentf-mode "enabled" "disabled"))) + :prompt "Find recent file: " + :sort nil + :require-match t + :category 'file + :state (consult--file-preview) + :history 'file-name-history))) + +;;;;; Command: consult-mode-command + +(defun consult--mode-name (mode) + "Return name part of MODE." + (replace-regexp-in-string + "global-\\(.*\\)-mode" "\\1" + (replace-regexp-in-string + "\\(-global\\)?-mode\\'" "" + (if (eq mode 'c-mode) + "cc" + (symbol-name mode)) + 'fixedcase) + 'fixedcase)) + +(defun consult--mode-command-candidates (modes) + "Extract commands from MODES. + +The list of features is searched for files belonging to the modes. +From these files, the commands are extracted." + (let* ((case-fold-search) + (buffer (current-buffer)) + (command-filter (consult--regexp-filter (seq-filter #'stringp consult-mode-command-filter))) + (feature-filter (seq-filter #'symbolp consult-mode-command-filter)) + (minor-hash (consult--string-hash minor-mode-list)) + (minor-local-modes (seq-filter (lambda (m) + (and (gethash m minor-hash) + (local-variable-if-set-p m))) + modes)) + (minor-global-modes (seq-filter (lambda (m) + (and (gethash m minor-hash) + (not (local-variable-if-set-p m)))) + modes)) + (major-modes (seq-remove (lambda (m) + (gethash m minor-hash)) + modes)) + (major-paths-hash (consult--string-hash (mapcar #'symbol-file major-modes))) + (minor-local-paths-hash (consult--string-hash (mapcar #'symbol-file minor-local-modes))) + (minor-global-paths-hash (consult--string-hash (mapcar #'symbol-file minor-global-modes))) + (major-name-regexp (regexp-opt (mapcar #'consult--mode-name major-modes))) + (minor-local-name-regexp (regexp-opt (mapcar #'consult--mode-name minor-local-modes))) + (minor-global-name-regexp (regexp-opt (mapcar #'consult--mode-name minor-global-modes))) + (commands)) + (dolist (feature load-history commands) + (when-let (name (alist-get 'provide feature)) + (let* ((path (car feature)) + (file (file-name-nondirectory path)) + (key (cond + ((memq name feature-filter) nil) + ((or (gethash path major-paths-hash) + (string-match-p major-name-regexp file)) + ?m) + ((or (gethash path minor-local-paths-hash) + (string-match-p minor-local-name-regexp file)) + ?l) + ((or (gethash path minor-global-paths-hash) + (string-match-p minor-global-name-regexp file)) + ?g)))) + (when key + (dolist (cmd (cdr feature)) + (let ((sym (cdr-safe cmd))) + (when (and (consp cmd) + (eq (car cmd) 'defun) + (commandp sym) + (not (get sym 'byte-obsolete-info)) + ;; Emacs 28 has a `read-extended-command-predicate' + (if (bound-and-true-p read-extended-command-predicate) + (funcall read-extended-command-predicate sym buffer) + t)) + (let ((name (symbol-name sym))) + (unless (string-match-p command-filter name) + (push (propertize name + 'consult--candidate sym + 'consult--type key) + commands)))))))))))) + +;;;###autoload +(defun consult-mode-command (&rest modes) + "Run a command from any of the given MODES. + +If no MODES are specified, use currently active major and minor modes." + (interactive) + (unless modes + (setq modes (cons major-mode + (seq-filter (lambda (m) + (and (boundp m) (symbol-value m))) + minor-mode-list)))) + (let ((narrow `((?m . ,(format "Major: %s" major-mode)) + (?l . "Local Minor") + (?g . "Global Minor")))) + (command-execute + (consult--read + (consult--mode-command-candidates modes) + :prompt "Mode command: " + :predicate + (lambda (cand) + (let ((key (get-text-property 0 'consult--type cand))) + (if consult--narrow + (= key consult--narrow) + (/= key ?g)))) + :lookup #'consult--lookup-candidate + :group (consult--type-group narrow) + :narrow narrow + :require-match t + :history 'extended-command-history + :category 'command)))) + +;;;;; Command: consult-yank + +(defun consult--read-from-kill-ring () + "Open kill ring menu and return selected string." + ;; `current-kill' updates `kill-ring' with interprogram paste, see + ;; gh:minad/consult#443. + (current-kill 0) + ;; Do not specify a :lookup function in order to preserve completion-styles + ;; highlighting of the current candidate. We have to perform a final lookup to + ;; obtain the original candidate which may be propertized with yank-specific + ;; properties, like 'yank-handler. + (consult--lookup-member + (consult--read + (consult--remove-dups + (or (if consult-yank-rotate + (append kill-ring-yank-pointer + (butlast kill-ring (length kill-ring-yank-pointer))) + kill-ring) + (user-error "Kill ring is empty"))) + :prompt "Yank from kill-ring: " + :history t ;; disable history + :sort nil + :category 'kill-ring + :require-match t + :state + (consult--insertion-preview + (point) + ;; If previous command is yank, hide previously yanked string + (or (and (eq last-command 'yank) (mark t)) (point)))) + kill-ring)) + +;; Adapted from the Emacs `yank-from-kill-ring' function. +;;;###autoload +(defun consult-yank-from-kill-ring (string &optional arg) + "Select STRING from the kill ring and insert it. +With prefix ARG, put point at beginning, and mark at end, like `yank' does. + +This command behaves like `yank-from-kill-ring' in Emacs 28, which also offers +a `completing-read' interface to the `kill-ring'. Additionally the Consult +version supports preview of the selected string." + (interactive (list (consult--read-from-kill-ring) current-prefix-arg)) + (when string + (setq yank-window-start (window-start)) + (push-mark) + (insert-for-yank string) + (setq this-command 'yank) + (when consult-yank-rotate + (if-let (pos (seq-position kill-ring string)) + (setq kill-ring-yank-pointer (nthcdr pos kill-ring)) + (kill-new string))) + (when (consp arg) + ;; Swap point and mark like in `yank'. + (goto-char (prog1 (mark t) + (set-marker (mark-marker) (point) (current-buffer))))))) + +(put 'consult-yank-replace 'delete-selection 'yank) +(put 'consult-yank-pop 'delete-selection 'yank) +(put 'consult-yank-from-kill-ring 'delete-selection 'yank) + +;;;###autoload +(defun consult-yank-pop (&optional arg) + "If there is a recent yank act like `yank-pop'. + +Otherwise select string from the kill ring and insert it. +See `yank-pop' for the meaning of ARG. + +This command behaves like `yank-pop' in Emacs 28, which also offers a +`completing-read' interface to the `kill-ring'. Additionally the Consult +version supports preview of the selected string." + (interactive "*p") + (if (eq last-command 'yank) + (yank-pop (or arg 1)) + (call-interactively #'consult-yank-from-kill-ring))) + +;; Adapted from the Emacs yank-pop function. +;;;###autoload +(defun consult-yank-replace (string) + "Select STRING from the kill ring. + +If there was no recent yank, insert the string. +Otherwise replace the just-yanked string with the selected string. + +There exists no equivalent of this command in Emacs 28." + (interactive (list (consult--read-from-kill-ring))) + (when string + (if (not (eq last-command 'yank)) + (consult-yank-from-kill-ring string) + (let ((inhibit-read-only t) + (pt (point)) + (mk (mark t))) + (setq this-command 'yank) + (funcall (or yank-undo-function 'delete-region) (min pt mk) (max pt mk)) + (setq yank-undo-function nil) + (set-marker (mark-marker) pt (current-buffer)) + (insert-for-yank string) + (set-window-start (selected-window) yank-window-start t) + (if (< pt mk) + (goto-char (prog1 (mark t) + (set-marker (mark-marker) (point) (current-buffer))))))))) + +;;;;; Command: consult-bookmark + +(defun consult--bookmark-preview () + "Create preview function for bookmarks." + (let ((preview (consult--jump-preview)) + (open (consult--temporary-files))) + (lambda (action cand) + (unless cand + (funcall open)) + (funcall + preview action + ;; Only preview bookmarks with the default handler. + (when-let ((bm (and cand (eq action 'preview) (assoc cand bookmark-alist))) + (handler (or (bookmark-get-handler bm) #'bookmark-default-handler)) + ((eq handler #'bookmark-default-handler)) + (file (bookmark-get-filename bm)) + (pos (bookmark-get-position bm)) + (buf (funcall open file))) + (set-marker (make-marker) pos buf)))))) + +(defun consult--bookmark-action (bm) + "Open BM via `consult--buffer-action'." + (bookmark-jump bm consult--buffer-display)) + +(consult--define-state bookmark) + +(defun consult--bookmark-candidates () + "Return bookmark candidates." + (bookmark-maybe-load-default-file) + (let ((narrow (cl-loop for (y _ . xs) in consult-bookmark-narrow nconc + (cl-loop for x in xs collect (cons x y))))) + (cl-loop for bm in bookmark-alist collect + (propertize (car bm) + 'consult--type + (alist-get + (or (bookmark-get-handler bm) #'bookmark-default-handler) + narrow))))) + +;;;###autoload +(defun consult-bookmark (name) + "If bookmark NAME exists, open it, otherwise create a new bookmark with NAME. + +The command supports preview of file bookmarks and narrowing. See the +variable `consult-bookmark-narrow' for the narrowing configuration." + (interactive + (list + (let ((narrow (cl-loop for (x y . _) in consult-bookmark-narrow collect (cons x y)))) + (consult--read + (consult--bookmark-candidates) + :prompt "Bookmark: " + :state (consult--bookmark-preview) + :category 'bookmark + :history 'bookmark-history + ;; Add default names to future history. + ;; Ignore errors such that `consult-bookmark' can be used in + ;; buffers which are not backed by a file. + :add-history (ignore-errors (bookmark-prop-get (bookmark-make-record) 'defaults)) + :group (consult--type-group narrow) + :narrow (consult--type-narrow narrow))))) + (bookmark-maybe-load-default-file) + (if (assoc name bookmark-alist) + (bookmark-jump name) + (bookmark-set name))) + +;;;;; Command: consult-complex-command + +;;;###autoload +(defun consult-complex-command () + "Select and evaluate command from the command history. + +This command can act as a drop-in replacement for `repeat-complex-command'." + (interactive) + (let* ((history (or (delete-dups (mapcar #'prin1-to-string command-history)) + (user-error "There are no previous complex commands"))) + (cmd (read (consult--read + history + :prompt "Command: " + :default (car history) + :sort nil + :history t ;; disable history + :category 'expression)))) + ;; Taken from `repeat-complex-command' + (add-to-history 'command-history cmd) + (apply #'funcall-interactively + (car cmd) + (mapcar (lambda (e) (eval e t)) (cdr cmd))))) + +;;;;; Command: consult-history + +(declare-function ring-elements "ring") + +(defun consult--current-history () + "Return the history and index variable relevant to the current buffer. +If the minibuffer is active, the minibuffer history is returned, +otherwise the history corresponding to the mode. There is a +special case for `repeat-complex-command', for which the command +history is used." + (cond + ;; In the minibuffer we use the current minibuffer history, + ;; which can be configured by setting `minibuffer-history-variable'. + ((minibufferp) + (when (eq minibuffer-history-variable t) + (user-error "Minibuffer history is disabled for `%s'" this-command)) + (list (mapcar #'consult--tofu-hide + (if (eq minibuffer-history-variable 'command-history) + ;; If pressing "C-x M-:", i.e., `repeat-complex-command', + ;; we are instead querying the `command-history' and get a + ;; full s-expression. Alternatively you might want to use + ;; `consult-complex-command', which can also be bound to + ;; "C-x M-:"! + (mapcar #'prin1-to-string command-history) + (symbol-value minibuffer-history-variable))))) + ;; Otherwise we use a mode-specific history, see `consult-mode-histories'. + (t (let ((found (seq-find (lambda (h) + (and (derived-mode-p (car h)) + (boundp (if (consp (cdr h)) (cadr h) (cdr h))))) + consult-mode-histories))) + (unless found + (user-error "No history configured for `%s', see `consult-mode-histories'" + major-mode)) + (cons (symbol-value (cadr found)) (cddr found)))))) + +;;;###autoload +(defun consult-history (&optional history index bol) + "Insert string from HISTORY of current buffer. +In order to select from a specific HISTORY, pass the history +variable as argument. INDEX is the name of the index variable to +update, if any. BOL is the function which jumps to the beginning +of the prompt. See also `cape-history' from the Cape package." + (interactive) + (pcase-let* ((`(,history ,index ,bol) (if history + (list history index bol) + (consult--current-history))) + (history (if (ring-p history) (ring-elements history) history)) + (`(,beg . ,end) + (if (minibufferp) + (cons (minibuffer-prompt-end) (point-max)) + (if bol + (save-excursion + (funcall bol) + (cons (point) (pos-eol))) + (cons (point) (point))))) + (str (consult--local-let ((enable-recursive-minibuffers t)) + (consult--read + (or (consult--remove-dups history) + (user-error "History is empty")) + :prompt "History: " + :history t ;; disable history + :category ;; Report category depending on history variable + (and (minibufferp) + (pcase minibuffer-history-variable + ('extended-command-history 'command) + ('buffer-name-history 'buffer) + ('face-name-history 'face) + ('read-envvar-name-history 'environment-variable) + ('bookmark-history 'bookmark) + ('file-name-history 'file))) + :sort nil + :initial (buffer-substring-no-properties beg end) + :state (consult--insertion-preview beg end))))) + (delete-region beg end) + (when index + (set index (seq-position history str))) + (insert (substring-no-properties str)))) + +;;;;; Command: consult-isearch-history + +(defun consult-isearch-forward (&optional reverse) + "Continue Isearch forward optionally in REVERSE." + (interactive) + (consult--require-minibuffer) + (setq isearch-new-forward (not reverse) isearch-new-nonincremental nil) + (funcall (or (command-remapping #'exit-minibuffer) #'exit-minibuffer))) + +(defun consult-isearch-backward (&optional reverse) + "Continue Isearch backward optionally in REVERSE." + (interactive) + (consult-isearch-forward (not reverse))) + +;; Emacs 28: hide in M-X +(put #'consult-isearch-backward 'completion-predicate #'ignore) +(put #'consult-isearch-forward 'completion-predicate #'ignore) + +(defvar-keymap consult-isearch-history-map + :doc "Additional keymap used by `consult-isearch-history'." + "<remap> <isearch-forward>" #'consult-isearch-forward + "<remap> <isearch-backward>" #'consult-isearch-backward) + +(defun consult--isearch-history-candidates () + "Return Isearch history candidates." + ;; Do not throw an error on empty history, in order to allow starting a + ;; search. We do not :require-match here. + (let ((history (if (eq t search-default-mode) + (append regexp-search-ring search-ring) + (append search-ring regexp-search-ring)))) + (delete-dups + (mapcar + (lambda (cand) + ;; The search type can be distinguished via text properties. + (let* ((props (plist-member (text-properties-at 0 cand) + 'isearch-regexp-function)) + (type (pcase (cadr props) + ((and 'nil (guard (not props))) ?r) + ('nil ?l) + ('word-search-regexp ?w) + ('isearch-symbol-regexp ?s) + ('char-fold-to-regexp ?c) + (_ ?u)))) + ;; Disambiguate history items. The same string could + ;; occur with different search types. + (consult--tofu-append cand type))) + history)))) + +(defconst consult--isearch-history-narrow + '((?c . "Char") + (?u . "Custom") + (?l . "Literal") + (?r . "Regexp") + (?s . "Symbol") + (?w . "Word"))) + +;;;###autoload +(defun consult-isearch-history () + "Read a search string with completion from the Isearch history. + +This replaces the current search string if Isearch is active, and +starts a new Isearch session otherwise." + (interactive) + (consult--forbid-minibuffer) + (let* ((isearch-message-function #'ignore) + (cursor-in-echo-area t) ;; Avoid cursor flickering + (candidates (consult--isearch-history-candidates))) + (unless isearch-mode (isearch-mode t)) + (with-isearch-suspended + (setq isearch-new-string + (consult--read + candidates + :prompt "I-search: " + :category 'consult-isearch-history + :history t ;; disable history + :sort nil + :initial isearch-string + :keymap consult-isearch-history-map + :annotate + (lambda (cand) + (consult--annotate-align + cand + (alist-get (consult--tofu-get cand) consult--isearch-history-narrow))) + :group + (lambda (cand transform) + (if transform + cand + (alist-get (consult--tofu-get cand) consult--isearch-history-narrow))) + :lookup + (lambda (selected candidates &rest _) + (if-let (found (member selected candidates)) + (substring (car found) 0 -1) + selected)) + :state + (lambda (action cand) + (when (and (eq action 'preview) cand) + (setq isearch-string cand) + (isearch-update-from-string-properties cand) + (isearch-update))) + :narrow + (list :predicate + (lambda (cand) (= (consult--tofu-get cand) consult--narrow)) + :keys consult--isearch-history-narrow)) + isearch-new-message + (mapconcat 'isearch-text-char-description isearch-new-string ""))) + ;; Setting `isearch-regexp' etc only works outside of `with-isearch-suspended'. + (unless (plist-member (text-properties-at 0 isearch-string) 'isearch-regexp-function) + (setq isearch-regexp t + isearch-regexp-function nil)))) + +;;;;; Command: consult-minor-mode-menu + +(defun consult--minor-mode-candidates () + "Return list of minor-mode candidate strings." + (mapcar + (pcase-lambda (`(,name . ,sym)) + (propertize + name + 'consult--candidate sym + 'consult--minor-mode-narrow + (logior + (ash (if (local-variable-if-set-p sym) ?l ?g) 8) + (if (and (boundp sym) (symbol-value sym)) ?i ?o)) + 'consult--minor-mode-group + (concat + (if (local-variable-if-set-p sym) "Local " "Global ") + (if (and (boundp sym) (symbol-value sym)) "On" "Off")))) + (nconc + ;; according to describe-minor-mode-completion-table-for-symbol + ;; the minor-mode-list contains *all* minor modes + (mapcar (lambda (sym) (cons (symbol-name sym) sym)) minor-mode-list) + ;; take the lighters from minor-mode-alist + (delq nil + (mapcar (pcase-lambda (`(,sym ,lighter)) + (when (and lighter (not (equal "" lighter))) + (let (message-log-max) + (setq lighter (string-trim (format-mode-line lighter))) + (unless (string-blank-p lighter) + (cons lighter sym))))) + minor-mode-alist))))) + +(defconst consult--minor-mode-menu-narrow + '((?l . "Local") + (?g . "Global") + (?i . "On") + (?o . "Off"))) + +;;;###autoload +(defun consult-minor-mode-menu () + "Enable or disable minor mode. + +This is an alternative to `minor-mode-menu-from-indicator'." + (interactive) + (call-interactively + (consult--read + (consult--minor-mode-candidates) + :prompt "Minor mode: " + :require-match t + :category 'minor-mode + :group + (lambda (cand transform) + (if transform cand (get-text-property 0 'consult--minor-mode-group cand))) + :narrow + (list :predicate + (lambda (cand) + (let ((narrow (get-text-property 0 'consult--minor-mode-narrow cand))) + (or (= (logand narrow 255) consult--narrow) + (= (ash narrow -8) consult--narrow)))) + :keys + consult--minor-mode-menu-narrow) + :lookup #'consult--lookup-candidate + :history 'consult--minor-mode-menu-history))) + +;;;;; Command: consult-theme + +;;;###autoload +(defun consult-theme (theme) + "Disable current themes and enable THEME from `consult-themes'. + +The command supports previewing the currently selected theme." + (interactive + (list + (let* ((regexp (consult--regexp-filter + (mapcar (lambda (x) (if (stringp x) x (format "\\`%s\\'" x))) + consult-themes))) + (avail-themes (seq-filter + (lambda (x) (string-match-p regexp (symbol-name x))) + (cons 'default (custom-available-themes)))) + (saved-theme (car custom-enabled-themes))) + (consult--read + (mapcar #'symbol-name avail-themes) + :prompt "Theme: " + :require-match t + :category 'theme + :history 'consult--theme-history + :lookup (lambda (selected &rest _) + (setq selected (and selected (intern-soft selected))) + (or (and selected (car (memq selected avail-themes))) + saved-theme)) + :state (lambda (action theme) + (pcase action + ('return (consult-theme (or theme saved-theme))) + ((and 'preview (guard theme)) (consult-theme theme)))) + :default (symbol-name (or saved-theme 'default)))))) + (when (eq theme 'default) (setq theme nil)) + (unless (eq theme (car custom-enabled-themes)) + (mapc #'disable-theme custom-enabled-themes) + (when theme + (if (custom-theme-p theme) + (enable-theme theme) + (load-theme theme :no-confirm))))) + +;;;;; Command: consult-buffer + +(defun consult--buffer-sort-alpha (buffers) + "Sort BUFFERS alphabetically, put starred buffers at the end." + (sort buffers + (lambda (x y) + (setq x (buffer-name x) y (buffer-name y)) + (let ((a (and (length> x 0) (eq (aref x 0) ?*))) + (b (and (length> y 0) (eq (aref y 0) ?*)))) + (if (eq a b) + (string< x y) + (not a)))))) + +(defun consult--buffer-sort-alpha-current (buffers) + "Sort BUFFERS alphabetically, put current at the beginning." + (let ((buffers (consult--buffer-sort-alpha buffers)) + (current (current-buffer))) + (if (memq current buffers) + (cons current (delq current buffers)) + buffers))) + +(defun consult--buffer-sort-visibility (buffers) + "Sort BUFFERS by visibility." + (let ((hidden) + (current (car (memq (current-buffer) buffers)))) + (consult--keep! buffers + (unless (eq it current) + (if (get-buffer-window it 'visible) + it + (push it hidden) + nil))) + (nconc (nreverse hidden) buffers (and current (list current))))) + +(defun consult--normalize-directory (dir) + "Normalize directory DIR. +DIR can be project, nil or a path." + (cond + ((eq dir 'project) (consult--project-root)) + (dir (expand-file-name dir)))) + +(defun consult--buffer-query-prompt (prompt query) + "Return a list of buffers and create an appropriate prompt string. +Return a pair of a prompt string and a list of buffers. PROMPT +is the prefix of the prompt string. QUERY specifies the buffers +to search and is passed to `consult--buffer-query'." + (let* ((dir (plist-get query :directory)) + (ndir (consult--normalize-directory dir)) + (buffers (apply #'consult--buffer-query :directory ndir query)) + (count (length buffers))) + (cons (format "%s (%d buffer%s%s): " prompt count + (if (= count 1) "" "s") + (cond + ((and ndir (eq dir 'project)) + (format ", Project %s" (consult--project-name ndir))) + (ndir (concat ", " (consult--left-truncate-file ndir))) + (t ""))) + buffers))) + +(cl-defun consult--buffer-query (&key sort directory mode as predicate (filter t) + include (exclude consult-buffer-filter) + (buffer-list t)) + "Query for a list of matching buffers. +The function supports filtering by various criteria which are +used throughout Consult. In particular it is the backbone of +most `consult-buffer-sources'. +DIRECTORY can either be the symbol project or a file name. +SORT can be visibility, alpha or nil. +FILTER can be either t, nil or invert. +EXCLUDE is a list of regexps. +INCLUDE is a list of regexps. +MODE can be a mode or a list of modes to restrict the returned buffers. +PREDICATE is a predicate function. +BUFFER-LIST is the unfiltered list of buffers. +AS is a conversion function." + (let ((root (consult--normalize-directory directory))) + (setq buffer-list (if (eq buffer-list t) (buffer-list) (copy-sequence buffer-list))) + (when sort + (setq buffer-list (funcall (intern (format "consult--buffer-sort-%s" sort)) buffer-list))) + (when (or filter mode as root) + (let ((exclude-re (consult--regexp-filter exclude)) + (include-re (consult--regexp-filter include)) + (case-fold-search)) + (consult--keep! buffer-list + (and + (or (not mode) + (let ((mm (buffer-local-value 'major-mode it))) + (if (consp mode) + (seq-some (lambda (m) (provided-mode-derived-p mm m)) mode) + (provided-mode-derived-p mm mode)))) + (pcase-exhaustive filter + ('nil t) + ((or 't 'invert) + (eq (eq filter t) + (and + (or (not exclude) + (not (string-match-p exclude-re (buffer-name it)))) + (or (not include) + (not (not (string-match-p include-re (buffer-name it))))))))) + (or (not root) + (when-let (dir (buffer-local-value 'default-directory it)) + (string-prefix-p root + (if (and (/= 0 (length dir)) (eq (aref dir 0) ?/)) + dir + (expand-file-name dir))))) + (or (not predicate) (funcall predicate it)) + (if as (funcall as it) it))))) + buffer-list)) + +(defun consult--buffer-file-hash () + "Return hash table of all buffer file names." + (consult--string-hash (consult--buffer-query :as #'buffer-file-name))) + +(defun consult--buffer-pair (buffer) + "Return a pair of name of BUFFER and BUFFER." + (cons (buffer-name buffer) buffer)) + +(defun consult--buffer-preview () + "Buffer preview function." + (let ((orig-buf (window-buffer (consult--original-window))) + (orig-prev (copy-sequence (window-prev-buffers))) + (orig-next (copy-sequence (window-next-buffers))) + other-win) + (lambda (action cand) + (pcase action + ('exit + (set-window-prev-buffers other-win orig-prev) + (set-window-next-buffers other-win orig-next)) + ('preview + (when (and (eq consult--buffer-display #'switch-to-buffer-other-window) + (not other-win)) + (switch-to-buffer-other-window orig-buf 'norecord) + (setq other-win (selected-window))) + (let ((win (or other-win (selected-window))) + (buf (or (and cand (get-buffer cand)) orig-buf))) + (when (and (window-live-p win) (buffer-live-p buf) + (not (buffer-match-p consult-preview-excluded-buffers buf))) + (with-selected-window win + (unless (or orig-prev orig-next) + (setq orig-prev (copy-sequence (window-prev-buffers)) + orig-next (copy-sequence (window-next-buffers)))) + (switch-to-buffer buf 'norecord))))))))) + +(defun consult--buffer-action (buffer &optional norecord) + "Switch to BUFFER via `consult--buffer-display' function. +If NORECORD is non-nil, do not record the buffer switch in the buffer list." + (funcall consult--buffer-display buffer norecord)) + +(consult--define-state buffer) + +(defvar consult--source-bookmark + `(:name "Bookmark" + :narrow ?m + :category bookmark + :face consult-bookmark + :history bookmark-history + :items ,#'bookmark-all-names + :state ,#'consult--bookmark-state) + "Bookmark candidate source for `consult-buffer'.") + +(defvar consult--source-project-buffer + `(:name "Project Buffer" + :narrow ?b + :category buffer + :face consult-buffer + :history buffer-name-history + :state ,#'consult--buffer-state + :enabled ,(lambda () consult-project-function) + :items + ,(lambda () + (when-let (root (consult--project-root)) + (consult--buffer-query :sort 'visibility + :directory root + :as #'consult--buffer-pair)))) + "Project buffer candidate source for `consult-buffer'.") + +(defvar consult--source-project-recent-file + `(:name "Project File" + :narrow ?f + :category file + :face consult-file + :history file-name-history + :state ,#'consult--file-state + :new + ,(lambda (file) + (consult--file-action + (expand-file-name file (consult--project-root)))) + :enabled + ,(lambda () + (and consult-project-function + recentf-mode)) + :items + ,(lambda () + (when-let (root (consult--project-root)) + (let ((len (length root)) + (ht (consult--buffer-file-hash)) + items) + (dolist (file (bound-and-true-p recentf-list) (nreverse items)) + ;; Emacs 29 abbreviates file paths by default, see + ;; `recentf-filename-handlers'. I recommend to set + ;; `recentf-filename-handlers' to nil to avoid any slow down. + (unless (eq (aref file 0) ?/) + (let (file-name-handler-alist) ;; No Tramp slowdown please. + (setq file (expand-file-name file)))) + (when (and (not (gethash file ht)) (string-prefix-p root file)) + (let ((part (substring file len))) + (when (equal part "") (setq part "./")) + (put-text-property 0 1 'multi-category `(file . ,file) part) + (push part items)))))))) + "Project file candidate source for `consult-buffer'.") + +(defvar consult--source-project-buffer-hidden + `(:hidden t :narrow (?p . "Project") ,@consult--source-project-buffer) + "Like `consult--source-project-buffer' but hidden by default.") + +(defvar consult--source-project-recent-file-hidden + `(:hidden t :narrow (?p . "Project") ,@consult--source-project-recent-file) + "Like `consult--source-project-recent-file' but hidden by default.") + +(defvar consult--source-hidden-buffer + `(:name "Hidden Buffer" + :narrow ?\s + :hidden t + :category buffer + :face consult-buffer + :history buffer-name-history + :action ,#'consult--buffer-action + :items + ,(lambda () (consult--buffer-query :sort 'visibility + :filter 'invert + :as #'consult--buffer-pair))) + "Hidden buffer candidate source for `consult-buffer'.") + +(defvar consult--source-modified-buffer + `(:name "Modified Buffer" + :narrow ?* + :hidden t + :category buffer + :face consult-buffer + :history buffer-name-history + :state ,#'consult--buffer-state + :items + ,(lambda () (consult--buffer-query :sort 'visibility + :as #'consult--buffer-pair + :predicate + (lambda (buf) + (and (buffer-modified-p buf) + (buffer-file-name buf)))))) + "Modified buffer candidate source for `consult-buffer'.") + +(defvar consult--source-buffer + `(:name "Buffer" + :narrow ?b + :category buffer + :face consult-buffer + :history buffer-name-history + :state ,#'consult--buffer-state + :default t + :items + ,(lambda () (consult--buffer-query :sort 'visibility + :as #'consult--buffer-pair))) + "Buffer candidate source for `consult-buffer'.") + +(defun consult--file-register-p (reg) + "Return non-nil if REG is a file register." + (memq (car-safe (cdr reg)) '(file-query file))) + +(autoload 'consult-register--candidates "consult-register") +(defvar consult--source-file-register + `(:name "File Register" + :narrow (?r . "Register") + :category file + :state ,#'consult--file-state + :enabled ,(lambda () (seq-some #'consult--file-register-p register-alist)) + :items ,(lambda () (consult-register--candidates #'consult--file-register-p))) + "File register source.") + +(defvar consult--source-recent-file + `(:name "File" + :narrow ?f + :category file + :face consult-file + :history file-name-history + :state ,#'consult--file-state + :new ,#'consult--file-action + :enabled ,(lambda () recentf-mode) + :items + ,(lambda () + (let ((ht (consult--buffer-file-hash)) + items) + (dolist (file (bound-and-true-p recentf-list) (nreverse items)) + ;; Emacs 29 abbreviates file paths by default, see + ;; `recentf-filename-handlers'. I recommend to set + ;; `recentf-filename-handlers' to nil to avoid any slow down. + (unless (eq (aref file 0) ?/) + (let (file-name-handler-alist) ;; No Tramp slowdown please. + (setq file (expand-file-name file)))) + (unless (gethash file ht) + (push (consult--fast-abbreviate-file-name file) items)))))) + "Recent file candidate source for `consult-buffer'.") + +;;;###autoload +(defun consult-buffer (&optional sources) + "Enhanced `switch-to-buffer' command with support for virtual buffers. + +The command supports recent files, bookmarks, views and project files as +virtual buffers. Buffers are previewed. Narrowing to buffers (b), files (f), +bookmarks (m) and project files (p) is supported via the corresponding +keys. In order to determine the project-specific files and buffers, the +`consult-project-function' is used. The virtual buffer SOURCES +default to `consult-buffer-sources'. See `consult--multi' for the +configuration of the virtual buffer sources." + (interactive) + (let ((selected (consult--multi (or sources consult-buffer-sources) + :require-match + (confirm-nonexistent-file-or-buffer) + :prompt "Switch to: " + :history 'consult--buffer-history + :sort nil))) + ;; For non-matching candidates, fall back to buffer creation. + (unless (plist-get (cdr selected) :match) + (consult--buffer-action (car selected))))) + +(defmacro consult--with-project (&rest body) + "Ensure that BODY is executed with a project root." + ;; We have to work quite hard here to ensure that the project root is + ;; only overridden at the current recursion level. When entering a + ;; recursive minibuffer session, we should be able to still switch the + ;; project. But who does that? Working on the first level on project A + ;; and on the second level on project B and on the third level on project C? + ;; You mustn't be afraid to dream a little bigger, darling. + `(let ((consult-project-function + (let ((root (or (consult--project-root t) (user-error "No project found"))) + (depth (recursion-depth)) + (orig consult-project-function)) + (lambda (may-prompt) + (if (= depth (recursion-depth)) + root + (funcall orig may-prompt)))))) + ,@body)) + +;;;###autoload +(defun consult-project-buffer () + "Enhanced `project-switch-to-buffer' command with support for virtual buffers. +The command may prompt you for a project directory if it is invoked from +outside a project. See `consult-buffer' for more details." + (interactive) + (consult--with-project + (consult-buffer consult-project-buffer-sources))) + +;;;###autoload +(defun consult-buffer-other-window () + "Variant of `consult-buffer', switching to a buffer in another window." + (interactive) + (let ((consult--buffer-display #'switch-to-buffer-other-window)) + (consult-buffer))) + +;;;###autoload +(defun consult-buffer-other-frame () + "Variant of `consult-buffer', switching to a buffer in another frame." + (interactive) + (let ((consult--buffer-display #'switch-to-buffer-other-frame)) + (consult-buffer))) + +;;;###autoload +(defun consult-buffer-other-tab () + "Variant of `consult-buffer', switching to a buffer in another tab." + (interactive) + (let ((consult--buffer-display #'switch-to-buffer-other-tab)) + (consult-buffer))) + +;;;;; Command: consult-grep + +(defun consult--grep-format (async builder) + "Return ASYNC function highlighting grep match results. +BUILDER is the command line builder function." + (let (highlight) + (lambda (action) + (cond + ((stringp action) + (setq highlight (cdr (funcall builder action))) + (funcall async action)) + ((consp action) + (let ((file "") (file-len 0) result) + (save-match-data + (dolist (str action) + (when (and (string-match consult--grep-match-regexp str) + ;; Filter out empty context lines + (or (/= (aref str (match-beginning 3)) ?-) + (/= (match-end 0) (length str)))) + ;; We share the file name across candidates to reduce + ;; the amount of allocated memory. + (unless (and (= file-len (- (match-end 1) (match-beginning 1))) + (eq t (compare-strings + file 0 file-len + str (match-beginning 1) (match-end 1) nil))) + (setq file (match-string 1 str) + file-len (length file))) + (let* ((line (match-string 2 str)) + (ctx (= (aref str (match-beginning 3)) ?-)) + (sep (if ctx "-" ":")) + (content (substring str (match-end 0))) + (line-len (length line))) + (when (length> content consult-grep-max-columns) + (setq content (substring content 0 consult-grep-max-columns))) + (when highlight + (funcall highlight content)) + (setq str (concat file sep line sep content)) + ;; Store file name in order to avoid allocations in `consult--prefix-group' + (add-text-properties 0 file-len `(face consult-file consult--prefix-group ,file) str) + (put-text-property (1+ file-len) (+ 1 file-len line-len) 'face 'consult-line-number str) + (when ctx + (add-face-text-property (+ 2 file-len line-len) (length str) 'consult-grep-context 'append str)) + (push str result))))) + (funcall async (nreverse result)))) + (t (funcall async action)))))) + +(defun consult--grep-position (cand &optional find-file) + "Return the grep position marker for CAND. +FIND-FILE is the file open function, defaulting to `find-file-noselect'." + (when cand + (let* ((file-end (next-single-property-change 0 'face cand)) + (line-end (next-single-property-change (1+ file-end) 'face cand)) + (matches (consult--point-placement cand (1+ line-end) 'consult-grep-context)) + (file (substring-no-properties cand 0 file-end)) + (line (string-to-number (substring-no-properties cand (+ 1 file-end) line-end)))) + (when-let (pos (consult--marker-from-line-column + (funcall (or find-file #'consult--file-action) file) + line (or (car matches) 0))) + (cons pos (cdr matches)))))) + +(defun consult--grep-state () + "Grep state function." + (let ((open (consult--temporary-files)) + (jump (consult--jump-state))) + (lambda (action cand) + (unless cand + (funcall open)) + (funcall jump action (consult--grep-position + cand + (and (not (eq action 'return)) open)))))) + +(defun consult--grep-exclude-args () + "Produce grep exclude arguments. +Take the variables `grep-find-ignored-directories' and +`grep-find-ignored-files' into account." + (unless (boundp 'grep-find-ignored-files) (require 'grep)) + (nconc (mapcar (lambda (s) (concat "--exclude=" s)) + (bound-and-true-p grep-find-ignored-files)) + (mapcar (lambda (s) (concat "--exclude-dir=" s)) + (bound-and-true-p grep-find-ignored-directories)))) + +(defun consult--grep (prompt make-builder dir initial) + "Run asynchronous grep. + +MAKE-BUILDER is the function that returns the command line +builder function. DIR is a directory or a list of file or +directories. PROMPT is the prompt string. INITIAL is initial +input." + (pcase-let* ((`(,prompt ,paths ,dir) (consult--directory-prompt prompt dir)) + (default-directory dir) + (builder (funcall make-builder paths))) + (consult--read + (consult--async-command builder + (consult--grep-format builder) + :file-handler t) ;; allow tramp + :prompt prompt + :lookup #'consult--lookup-member + :state (consult--grep-state) + :initial (consult--async-split-initial initial) + :add-history (consult--async-split-thingatpt 'symbol) + :require-match t + :category 'consult-grep + :group #'consult--prefix-group + :history '(:input consult--grep-history) + :sort nil))) + +(defun consult--grep-lookahead-p (&rest cmd) + "Return t if grep CMD supports look-ahead." + (eq 0 (process-file-shell-command + (concat "echo xaxbx | " + (mapconcat #'shell-quote-argument `(,@cmd "^(?=.*b)(?=.*a)") " "))))) + +(defun consult--grep-make-builder (paths) + "Build grep command line and grep across PATHS." + (let* ((cmd (consult--build-args consult-grep-args)) + (type (if (consult--grep-lookahead-p (car cmd) "-P") 'pcre 'extended))) + (lambda (input) + (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) + (flags (append cmd opts)) + (ignore-case (or (member "-i" flags) (member "--ignore-case" flags)))) + (if (or (member "-F" flags) (member "--fixed-strings" flags)) + (cons (append cmd (list "-e" arg) opts paths) + (apply-partially #'consult--highlight-regexps + (list (regexp-quote arg)) ignore-case)) + (pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg type ignore-case))) + (when re + (cons (append cmd + (list (if (eq type 'pcre) "-P" "-E") ;; perl or extended + "-e" (consult--join-regexps re type)) + opts paths) + hl)))))))) + +;;;###autoload +(defun consult-grep (&optional dir initial) + "Search with `grep' for files in DIR where the content matches a regexp. + +The initial input is given by the INITIAL argument. DIR can be +nil, a directory string or a list of file/directory paths. If +`consult-grep' is called interactively with a prefix argument, +the user can specify the directories or files to search in. +Multiple directories must be separated by comma in the +minibuffer, since they are read via `completing-read-multiple'. +By default the project directory is used if +`consult-project-function' is defined and returns non-nil. +Otherwise the `default-directory' is searched. + +The input string is split, the first part of the string (grep +input) is passed to the asynchronous grep process and the second +part of the string is passed to the completion-style filtering. + +The input string is split at a punctuation character, which is +given as the first character of the input string. The format is +similar to Perl-style regular expressions, e.g., /regexp/. +Furthermore command line options can be passed to grep, specified +behind --. The overall prompt input has the form +`#async-input -- grep-opts#filter-string'. + +Note that the grep input string is transformed from Emacs regular +expressions to Posix regular expressions. Always enter Emacs +regular expressions at the prompt. `consult-grep' behaves like +builtin Emacs search commands, e.g., Isearch, which take Emacs +regular expressions. Furthermore the asynchronous input split +into words, each word must match separately and in any order. +See `consult--regexp-compiler' for the inner workings. In order +to disable transformations of the grep input, adjust +`consult--regexp-compiler' accordingly. + +Here we give a few example inputs: + +#alpha beta : Search for alpha and beta in any order. +#alpha.*beta : Search for alpha before beta. +#\\(alpha\\|beta\\) : Search for alpha or beta (Note Emacs syntax!) +#word -- -C3 : Search for word, include 3 lines as context +#first#second : Search for first, quick filter for second. + +The symbol at point is added to the future history." + (interactive "P") + (consult--grep "Grep" #'consult--grep-make-builder dir initial)) + +;;;;; Command: consult-git-grep + +(defun consult--git-grep-make-builder (paths) + "Create grep command line builder given PATHS." + (let ((cmd (consult--build-args consult-git-grep-args))) + (lambda (input) + (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) + (flags (append cmd opts)) + (ignore-case (or (member "-i" flags) (member "--ignore-case" flags)))) + (if (or (member "-F" flags) (member "--fixed-strings" flags)) + (cons (append cmd (list "-e" arg) opts paths) + (apply-partially #'consult--highlight-regexps + (list (regexp-quote arg)) ignore-case)) + (pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg 'extended ignore-case))) + (when re + (cons (append cmd + (cdr (mapcan (lambda (x) (list "--and" "-e" x)) re)) + opts paths) + hl)))))))) + +;;;###autoload +(defun consult-git-grep (&optional dir initial) + "Search with `git grep' for files in DIR with INITIAL input. +See `consult-grep' for details." + (interactive "P") + (consult--grep "Git-grep" #'consult--git-grep-make-builder dir initial)) + +;;;;; Command: consult-ripgrep + +(defun consult--ripgrep-make-builder (paths) + "Create ripgrep command line builder given PATHS." + (let* ((cmd (consult--build-args consult-ripgrep-args)) + (type (if (consult--grep-lookahead-p (car cmd) "-P") 'pcre 'extended))) + (lambda (input) + (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) + (flags (append cmd opts)) + (ignore-case + (and (not (or (member "-s" flags) (member "--case-sensitive" flags))) + (or (member "-i" flags) (member "--ignore-case" flags) + (and (or (member "-S" flags) (member "--smart-case" flags)) + (let (case-fold-search) + ;; Case insensitive if there are no uppercase letters + (not (string-match-p "[[:upper:]]" arg)))))))) + (if (or (member "-F" flags) (member "--fixed-strings" flags)) + (cons (append cmd (list "-e" arg) opts paths) + (apply-partially #'consult--highlight-regexps + (list (regexp-quote arg)) ignore-case)) + (pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg type ignore-case))) + (when re + (cons (append cmd (and (eq type 'pcre) '("-P")) + (list "-e" (consult--join-regexps re type)) + opts paths) + hl)))))))) + +;;;###autoload +(defun consult-ripgrep (&optional dir initial) + "Search with `rg' for files in DIR with INITIAL input. +See `consult-grep' for details." + (interactive "P") + (consult--grep "Ripgrep" #'consult--ripgrep-make-builder dir initial)) + +;;;;; Command: consult-find + +(defun consult--find (prompt builder initial) + "Run find command in current directory. + +The function returns the selected file. +The filename at point is added to the future history. + +BUILDER is the command line builder function. +PROMPT is the prompt. +INITIAL is initial input." + (consult--read + (consult--async-command builder + (consult--async-map (lambda (x) (string-remove-prefix "./" x))) + (consult--async-highlight builder) + :file-handler t) ;; allow tramp + :prompt prompt + :sort nil + :require-match t + :initial (consult--async-split-initial initial) + :add-history (consult--async-split-thingatpt 'filename) + :category 'file + :history '(:input consult--find-history))) + +(defun consult--find-make-builder (paths) + "Build find command line, finding across PATHS." + (let* ((cmd (seq-mapcat (lambda (x) + (if (equal x ".") paths (list x))) + (consult--build-args consult-find-args))) + (type (if (eq 0 (process-file-shell-command + (concat (car cmd) " -regextype emacs -version"))) + 'emacs 'basic))) + (lambda (input) + (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) + ;; ignore-case=t since -iregex is used below + (`(,re . ,hl) (funcall consult--regexp-compiler arg type t))) + (when re + (cons (append cmd + (cdr (mapcan + (lambda (x) + `("-and" "-iregex" + ,(format ".*%s.*" + ;; Replace non-capturing groups with capturing groups. + ;; GNU find does not support non-capturing groups. + (replace-regexp-in-string + "\\\\(\\?:" "\\(" x 'fixedcase 'literal)))) + re)) + opts) + hl)))))) + +;;;###autoload +(defun consult-find (&optional dir initial) + "Search for files with `find' in DIR. +The file names must match the input regexp. INITIAL is the +initial minibuffer input. See `consult-grep' for details +regarding the asynchronous search and the arguments." + (interactive "P") + (pcase-let* ((`(,prompt ,paths ,dir) (consult--directory-prompt "Find" dir)) + (default-directory dir) + (builder (consult--find-make-builder paths))) + (find-file (consult--find prompt builder initial)))) + +;;;;; Command: consult-fd + +(defun consult--fd-make-builder (paths) + "Build find command line, finding across PATHS." + (let ((cmd (consult--build-args consult-fd-args))) + (lambda (input) + (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) + (flags (append cmd opts)) + (ignore-case + (and (not (or (member "-s" flags) (member "--case-sensitive" flags))) + (or (member "-i" flags) (member "--ignore-case" flags) + (let (case-fold-search) + ;; Case insensitive if there are no uppercase letters + (not (string-match-p "[[:upper:]]" arg))))))) + (if (or (member "-F" flags) (member "--fixed-strings" flags)) + (cons (append cmd (list arg) opts paths) + (apply-partially #'consult--highlight-regexps + (list (regexp-quote arg)) ignore-case)) + (pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg 'pcre ignore-case))) + (when re + (cons (append cmd + (mapcan (lambda (x) `("--and" ,x)) re) + opts + (mapcan (lambda (x) `("--search-path" ,x)) paths)) + hl)))))))) + +;;;###autoload +(defun consult-fd (&optional dir initial) + "Search for files with `fd' in DIR. +The file names must match the input regexp. INITIAL is the +initial minibuffer input. See `consult-grep' for details +regarding the asynchronous search and the arguments." + (interactive "P") + (pcase-let* ((`(,prompt ,paths ,dir) (consult--directory-prompt "Fd" dir)) + (default-directory dir) + (builder (consult--fd-make-builder paths))) + (find-file (consult--find prompt builder initial)))) + +;;;;; Command: consult-locate + +(defun consult--locate-builder (input) + "Build command line from INPUT." + (pcase-let ((`(,arg . ,opts) (consult--command-split input))) + (unless (string-blank-p arg) + (cons (append (consult--build-args consult-locate-args) + (consult--split-escaped arg) opts) + (cdr (consult--default-regexp-compiler input 'basic t)))))) + +;;;###autoload +(defun consult-locate (&optional initial) + "Search with `locate' for files which match input given INITIAL input. + +The input is treated literally such that locate can take advantage of +the locate database index. Regular expressions would often force a slow +linear search through the entire database. The locate process is started +asynchronously, similar to `consult-grep'. See `consult-grep' for more +details regarding the asynchronous search." + (interactive) + (find-file (consult--find "Locate: " #'consult--locate-builder initial))) + +;;;;; Command: consult-man + +(defun consult--man-builder (input) + "Build command line from INPUT." + (pcase-let* ((`(,arg . ,opts) (consult--command-split input)) + (`(,re . ,hl) (funcall consult--regexp-compiler arg 'extended t))) + (when re + (cons (append (consult--build-args consult-man-args) + (list (consult--join-regexps re 'extended)) + opts) + hl)))) + +(defun consult--man-format (lines) + "Format man candidates from LINES." + (let ((candidates)) + (save-match-data + (dolist (str lines) + (when (string-match "\\`\\(.*?\\([^ ]+\\) *(\\([^,)]+\\)[^)]*).*?\\) +- +\\(.*\\)\\'" str) + (let* ((names (match-string 1 str)) + (name (match-string 2 str)) + (section (match-string 3 str)) + (desc (match-string 4 str)) + (cand (format "%s - %s" names desc))) + (add-text-properties 0 (length names) + (list 'face 'consult-file + 'consult-man (concat section " " name)) + cand) + (push cand candidates))))) + (nreverse candidates))) + +;;;###autoload +(defun consult-man (&optional initial) + "Search for man page given INITIAL input. + +The input string is not preprocessed and passed literally to the +underlying man commands. The man process is started asynchronously, +similar to `consult-grep'. See `consult-grep' for more details regarding +the asynchronous search." + (interactive) + (man (consult--read + (consult--async-command #'consult--man-builder + (consult--async-transform consult--man-format) + (consult--async-highlight #'consult--man-builder)) + :prompt "Manual entry: " + :require-match t + :category 'consult-man + :lookup (apply-partially #'consult--lookup-prop 'consult-man) + :initial (consult--async-split-initial initial) + :add-history (consult--async-split-thingatpt 'symbol) + :history '(:input consult--man-history)))) + +;;;; Preview at point in completions buffers + +(define-minor-mode consult-preview-at-point-mode + "Preview minor mode for *Completions* buffers. +When moving around in the *Completions* buffer, the candidate at point is +automatically previewed." + :group 'consult + (if consult-preview-at-point-mode + (add-hook 'post-command-hook #'consult-preview-at-point nil 'local) + (remove-hook 'post-command-hook #'consult-preview-at-point 'local))) + +(defun consult-preview-at-point () + "Preview candidate at point in *Completions* buffer." + (interactive) + (when-let ((win (active-minibuffer-window)) + (buf (window-buffer win)) + (fun (buffer-local-value 'consult--preview-function buf))) + (funcall fun))) + +;;;; Integration with completion systems + +;;;;; Integration: Default *Completions* + +(defun consult--default-completion-minibuffer-candidate () + "Return current minibuffer candidate from default completion system or Icomplete." + (when (and (minibufferp) + (eq completing-read-function #'completing-read-default)) + (let ((content (minibuffer-contents-no-properties))) + ;; When the current minibuffer content matches a candidate, return it! + (if (test-completion content + minibuffer-completion-table + minibuffer-completion-predicate) + content + ;; Return the full first candidate of the sorted completion list. + (when-let ((completions (completion-all-sorted-completions))) + (concat + (substring content 0 (or (cdr (last completions)) 0)) + (car completions))))))) + +(defun consult--default-completion-list-candidate () + "Return current candidate at point from completions buffer." + (let (beg end) + (when (and + (derived-mode-p 'completion-list-mode) + ;; Logic taken from `choose-completion'. + ;; TODO Upstream a `completion-list-get-candidate' function. + (cond + ((and (not (eobp)) (get-text-property (point) 'mouse-face)) + (setq end (point) beg (1+ (point)))) + ((and (not (bobp)) (get-text-property (1- (point)) 'mouse-face)) + (setq end (1- (point)) beg (point))))) + (setq beg (previous-single-property-change beg 'mouse-face) + end (or (next-single-property-change end 'mouse-face) (point-max))) + (or (get-text-property beg 'completion--string) + (buffer-substring-no-properties beg end))))) + +;;;;; Integration: Vertico + +(defvar vertico--input) +(declare-function vertico--exhibit "ext:vertico") +(declare-function vertico--candidate "ext:vertico") +(declare-function vertico--filter-completions "ext:vertico") + +(defun consult--vertico-candidate () + "Return current candidate for Consult preview." + (and vertico--input (vertico--candidate 'highlight))) + +(defun consult--vertico-refresh () + "Refresh completion UI." + (when vertico--input + (setq vertico--input t) + (vertico--exhibit))) + +(defun consult--vertico-filter-adv (orig pattern cands category highlight) + "Advice for ORIG `consult--completion-filter' function. +See `consult--completion-filter' for arguments PATTERN, CANDS, CATEGORY +and HIGHLIGHT." + (if (and (not highlight) (bound-and-true-p vertico-mode)) + ;; Optimize `consult--completion-filter' using the deferred highlighting + ;; from Vertico. The advice is not necessary - it is a pure optimization. + (nconc (car (vertico--filter-completions pattern cands nil (length pattern) + `(metadata (category . ,category)))) + nil) + (funcall orig pattern cands category highlight))) + +(with-eval-after-load 'vertico + (advice-add #'consult--completion-filter :around #'consult--vertico-filter-adv) + (add-hook 'consult--completion-candidate-hook #'consult--vertico-candidate) + (add-hook 'consult--completion-refresh-hook #'consult--vertico-refresh) + (define-key consult-async-map [remap vertico-insert] 'vertico-next-group)) + +;;;;; Integration: Mct + +(with-eval-after-load 'mct (add-hook 'consult--completion-refresh-hook + 'mct--live-completions-refresh)) + +;;;;; Integration: Icomplete + +(defvar icomplete-mode) +(declare-function icomplete-exhibit "icomplete") + +(defun consult--icomplete-refresh () + "Refresh icomplete view." + (when icomplete-mode + (let ((top (car completion-all-sorted-completions))) + (completion--flush-all-sorted-completions) + ;; force flushing, otherwise narrowing is broken! + (setq completion-all-sorted-completions nil) + (when top + (let* ((completions (completion-all-sorted-completions)) + (last (last completions)) + (before)) ;; completions before top + ;; warning: completions is an improper list + (while (consp completions) + (if (equal (car completions) top) + (progn + (setcdr last (append (nreverse before) (cdr last))) + (setq completion-all-sorted-completions completions + completions nil)) + (push (car completions) before) + (setq completions (cdr completions))))))) + (icomplete-exhibit))) + +(with-eval-after-load 'icomplete + (add-hook 'consult--completion-refresh-hook #'consult--icomplete-refresh)) + +(provide 'consult) +;;; consult.el ends here diff --git a/emacs/elpa/consult-20240811.1858/consult.elc b/emacs/elpa/consult-20240811.1858/consult.elc Binary files differ. diff --git a/emacs/elpa/doom-modeline-20240810.1702/doom-modeline-core.el b/emacs/elpa/doom-modeline-20240810.1702/doom-modeline-core.el @@ -1,1704 +0,0 @@ -;;; doom-modeline-core.el --- The core libraries for doom-modeline -*- lexical-binding: t; -*- - -;; Copyright (C) 2018-2024 Vincent Zhang - -;; This file is not part of GNU Emacs. - -;; -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. -;; -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see <https://www.gnu.org/licenses/>. -;; - -;;; Commentary: -;; -;; The core libraries for doom-modeline. -;; - -;;; Code: - -(require 'compat) -(eval-when-compile - (require 'cl-lib) - (require 'subr-x)) -(require 'nerd-icons) -(require 'shrink-path) - - -;; -;; Compatibility -;; - -(unless (boundp 'mode-line-right-align-edge) - (defcustom mode-line-right-align-edge 'window - "Where mode-line should align to. -Internally, that function uses `:align-to' in a display property, -so aligns to the left edge of the given area. See info node -`(elisp)Pixel Specification'. - -Must be set to a symbol. Acceptable values are: -- `window': align to extreme right of window, regardless of margins - or fringes -- `right-fringe': align to right-fringe -- `right-margin': align to right-margin" - :type '(choice (const right-margin) - (const right-fringe) - (const window)) - :group 'mode-line)) - - -;; -;; Optimization -;; - -;; Don’t compact font caches during GC. -(when (eq system-type 'windows-nt) - (setq inhibit-compacting-font-caches t)) - - -;; -;; Customization -;; - -(defgroup doom-modeline nil - "A minimal and modern mode-line." - :group 'mode-line - :link '(url-link :tag "Homepage" "https://github.com/seagle0128/doom-modeline")) - -(defcustom doom-modeline-support-imenu nil - "If non-nil, cause imenu to see `doom-modeline' declarations. -This is done by adjusting `lisp-imenu-generic-expression' to -include support for finding `doom-modeline-def-*' forms. - -Must be set before loading `doom-modeline'." - :type 'boolean - :set (lambda (_sym val) - (if val - (add-hook 'emacs-lisp-mode-hook #'doom-modeline-add-imenu) - (remove-hook 'emacs-lisp-mode-hook #'doom-modeline-add-imenu))) - :group 'doom-modeline) - -(defcustom doom-modeline-height (+ (frame-char-height) 4) - "How tall the mode-line should be. It's only respected in GUI. -If the actual char height is larger, it respects the actual char height." - :type 'integer - :group 'doom-modeline) - -(defcustom doom-modeline-bar-width 4 - "How wide the mode-line bar should be. It's only respected in GUI." - :type 'integer - :set (lambda (sym val) - (set sym (if (> val 0) val 1))) - :group 'doom-modeline) - -(defcustom doom-modeline-hud nil - "Whether to use hud instead of default bar. It's only respected in GUI." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-hud-min-height 2 - "Minimum height in pixels of the \"thumb\" of the hud. -Only respected in GUI." - :type 'integer - :set (lambda (sym val) - (set sym (if (> val 1) val 1))) - :group 'doom-modeline) - -(defcustom doom-modeline-window-width-limit 85 - "The limit of the window width. - -If `window-width' is smaller than the limit, some information won't be -displayed. It can be an integer or a float number. nil means no limit." - :type '(choice integer - float - (const :tag "Disable" nil)) - :group 'doom-modeline) - -(defcustom doom-modeline-project-detection 'auto - "How to detect the project root. - -nil means to use `default-directory'. - -The project management packages have some issues on detecting project root. -e.g. `projectile' doesn't handle symlink folders well, while `project' is -unable to handle sub-projects. -Specify another one if you encounter the issue." - :type '(choice (const :tag "Auto-detect" auto) - (const :tag "Find File in Project" ffip) - (const :tag "Projectile" projectile) - (const :tag "Built-in Project" project) - (const :tag "Disable" nil)) - :group 'doom-modeline) - -(defcustom doom-modeline-buffer-file-name-style 'auto - "Determines the style used by `doom-modeline-buffer-file-name'. - -Given ~/Projects/FOSS/emacs/lisp/comint.el - auto => emacs/l/comint.el (in a project) or comint.el - truncate-upto-project => ~/P/F/emacs/lisp/comint.el - truncate-from-project => ~/Projects/FOSS/emacs/l/comint.el - truncate-with-project => emacs/l/comint.el - truncate-except-project => ~/P/F/emacs/l/comint.el - truncate-upto-root => ~/P/F/e/lisp/comint.el - truncate-all => ~/P/F/e/l/comint.el - truncate-nil => ~/Projects/FOSS/emacs/lisp/comint.el - relative-from-project => emacs/lisp/comint.el - relative-to-project => lisp/comint.el - file-name => comint.el - file-name-with-project => FOSS|comint.el - buffer-name => comint.el<2> (uniquify buffer name)" - :type '(choice (const auto) - (const truncate-upto-project) - (const truncate-from-project) - (const truncate-with-project) - (const truncate-except-project) - (const truncate-upto-root) - (const truncate-all) - (const truncate-nil) - (const relative-from-project) - (const relative-to-project) - (const file-name) - (const file-name-with-project) - (const buffer-name)) - :group'doom-modeline) - -(defcustom doom-modeline-buffer-file-true-name nil - "Use `file-truename' on buffer file name. - -Project detection(projectile.el) may uses `file-truename' on directory path. -Turn on this to provide right relative path for buffer file name." - :type 'boolean - :group'doom-modeline) - -(defcustom doom-modeline-icon t - "Whether display the icons in the mode-line. - -While using the server mode in GUI, should set the value explicitly." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-major-mode-icon t - "Whether display the icon for `major-mode'. - -It respects option `doom-modeline-icon'." - :type 'boolean - :group'doom-modeline) - -(defcustom doom-modeline-major-mode-color-icon t - "Whether display the colorful icon for `major-mode'. - -It respects option `nerd-icons-color-icons'." - :type 'boolean - :group'doom-modeline) - -(defcustom doom-modeline-buffer-state-icon t - "Whether display the icon for the buffer state. - -It respects option `doom-modeline-icon'." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-buffer-modification-icon t - "Whether display the modification icon for the buffer. - -It respects option `doom-modeline-icon' and `doom-modeline-buffer-state-icon'." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-lsp-icon t - "Whether display the icon of lsp client. - -It respects option `doom-modeline-icon'." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-time-icon t - "Whether display the icon of time. - -It respects option `doom-modeline-icon'." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-time-live-icon t - "Whether display the live icons of time. - -It respects option `doom-modeline-icon' and option `doom-modeline-time-icon'." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-time-analogue-clock t - "Whether to draw an analogue clock SVG as the live time icon. -It respects the option `doom-modeline-icon', option `doom-modeline-time-icon', -and option `doom-modeline-time-live-icon'." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-time-clock-minute-resolution 1 - "The clock will be updated every this many minutes, truncated. -See `doom-modeline-time-analogue-clock'." - :type 'natnum - :group 'doom-modeline) - -(defcustom doom-modeline-time-clock-size 0.7 - "Size of the analogue clock drawn, either in pixels or as a proportional height. -An integer value is used as the diameter of clock in pixels. -A floating point value sets the diameter of the clock realtive to -`doom-modeline-height'. - -Only relevant when `doom-modeline-time-analogue-clock' is non-nil, which see." - :type 'number - :group 'doom-modeline) - -(defcustom doom-modeline-unicode-fallback nil - "Whether to use unicode as a fallback (instead of ASCII) when not using icons." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-buffer-name t - "Whether display the buffer name." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-highlight-modified-buffer-name t - "Whether highlight the modified buffer name." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-column-zero-based t - "When non-nil, mode line displays column numbers zero-based. -See `column-number-indicator-zero-based'." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-percent-position '(-3 "%p") - "Specification of \"percentage offset\" of window through buffer. -See `mode-line-percent-position'." - :type '(radio - (const :tag "nil: No offset is displayed" nil) - (const :tag "\"%o\": Proportion of \"travel\" of the window through the buffer" - (-3 "%o")) - (const :tag "\"%p\": Percentage offset of top of window" - (-3 "%p")) - (const :tag "\"%P\": Percentage offset of bottom of window" - (-3 "%P")) - (const :tag "\"%q\": Offsets of both top and bottom of window" - (6 "%q"))) - :group 'doom-modeline) - -(defcustom doom-modeline-position-line-format '("L%l") - "Format used to display line numbers in the mode line. -See `mode-line-position-line-format'." - :type '(list string) - :group 'doom-modeline) - -(defcustom doom-modeline-position-column-format '("C%c") - "Format used to display column numbers in the mode line. -See `mode-line-position-column-format'." - :type '(list string) - :group 'doom-modeline) - -(defcustom doom-modeline-position-column-line-format '("%l:%c") - "Format used to display combined line/column numbers in the mode line. -See `mode-line-position-column-line-format'." - :type '(list string) - :group 'doom-modeline) - -(defcustom doom-modeline-minor-modes nil - "Whether display the minor modes in the mode-line." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-enable-word-count nil - "If non-nil, a word count will be added to the selection-info modeline segment." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-continuous-word-count-modes - '(markdown-mode gfm-mode org-mode) - "Major modes in which to display word count continuously. - -It respects `doom-modeline-enable-word-count'." - :type '(repeat (symbol :tag "Major-Mode") ) - :group 'doom-modeline) - -(defcustom doom-modeline-buffer-encoding t - "Whether display the buffer encoding." - :type '(choice (const :tag "Always" t) - (const :tag "When non-default" nondefault) - (const :tag "Never" nil)) - :group 'doom-modeline) - -(defcustom doom-modeline-default-coding-system 'utf-8 - "Default coding system for `doom-modeline-buffer-encoding' `nondefault'." - :type 'coding-system - :group 'doom-modeline) - -(defcustom doom-modeline-default-eol-type 0 - "Default EOL type for `doom-modeline-buffer-encoding' `nondefault'." - :type '(choice (const :tag "Unix-style LF" 0) - (const :tag "DOS-style CRLF" 1) - (const :tag "Mac-style CR" 2)) - :group 'doom-modeline) - -(defcustom doom-modeline-indent-info nil - "Whether display the indentation information." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-total-line-number nil - "Whether display the total line number." - :type 'boolean - :group 'doom-modeline) - -;; It is based upon `editorconfig-indentation-alist' but is used to read indentation levels instead -;; of setting them. (https://github.com/editorconfig/editorconfig-emacs) -(defcustom doom-modeline-indent-alist - '((apache-mode apache-indent-level) - (awk-mode c-basic-offset) - (bpftrace-mode c-basic-offset) - (c++-mode c-basic-offset) - (c-mode c-basic-offset) - (cmake-mode cmake-tab-width) - (coffee-mode coffee-tab-width) - (cperl-mode cperl-indent-level) - (crystal-mode crystal-indent-level) - (csharp-mode c-basic-offset) - (css-mode css-indent-offset) - (d-mode c-basic-offset) - (emacs-lisp-mode lisp-indent-offset) - (enh-ruby-mode enh-ruby-indent-level) - (erlang-mode erlang-indent-level) - (ess-mode ess-indent-offset) - (f90-mode f90-associate-indent - f90-continuation-indent - f90-critical-indent - f90-do-indent - f90-if-indent - f90-program-indent - f90-type-indent) - (feature-mode feature-indent-offset - feature-indent-level) - (fsharp-mode fsharp-continuation-offset - fsharp-indent-level - fsharp-indent-offset) - (groovy-mode groovy-indent-offset) - (haskell-mode haskell-indent-spaces - haskell-indent-offset - haskell-indentation-layout-offset - haskell-indentation-left-offset - haskell-indentation-starter-offset - haskell-indentation-where-post-offset - haskell-indentation-where-pre-offset - shm-indent-spaces) - (haxor-mode haxor-tab-width) - (idl-mode c-basic-offset) - (jade-mode jade-tab-width) - (java-mode c-basic-offset) - (js-mode js-indent-level) - (js-jsx-mode js-indent-level - sgml-basic-offset) - (js2-mode js2-basic-offset) - (js2-jsx-mode js2-basic-offset - sgml-basic-offset) - (js3-mode js3-indent-level) - (json-mode js-indent-level) - (julia-mode julia-indent-offset) - (kotlin-mode kotlin-tab-width) - (latex-mode tex-indent-basic) - (lisp-mode lisp-indent-offset) - (livescript-mode livescript-tab-width) - (lua-mode lua-indent-level) - (matlab-mode matlab-indent-level) - (mips-mode mips-tab-width) - (mustache-mode mustache-basic-offset) - (nasm-mode nasm-basic-offset) - (nginx-mode nginx-indent-level) - (nxml-mode nxml-child-indent) - (objc-mode c-basic-offset) - (octave-mode octave-block-offset) - (perl-mode perl-indent-level) - (php-mode c-basic-offset) - (pike-mode c-basic-offset) - (ps-mode ps-mode-tab) - (pug-mode pug-tab-width) - (puppet-mode puppet-indent-level) - (python-mode python-indent-offset) - (ruby-mode ruby-indent-level) - (rust-mode rust-indent-offset) - (rustic-mode rustic-indent-offset) - (scala-mode scala-indent:step) - (scss-mode css-indent-offset) - (sgml-mode sgml-basic-offset) - (sh-mode sh-basic-offset - sh-indentation) - (slim-mode slim-indent-offset) - (sml-mode sml-indent-level) - (tcl-mode tcl-indent-level - tcl-continued-indent-level) - (terra-mode terra-indent-level) - (typescript-mode typescript-indent-level) - (verilog-mode verilog-indent-level - verilog-indent-level-behavioral - verilog-indent-level-declaration - verilog-indent-level-module - verilog-cexp-indent - verilog-case-indent) - (web-mode web-mode-attr-indent-offset - web-mode-attr-value-indent-offset - web-mode-code-indent-offset - web-mode-css-indent-offset - web-mode-markup-indent-offset - web-mode-sql-indent-offset - web-mode-block-padding - web-mode-script-padding - web-mode-style-padding) - (yaml-mode yaml-indent-offset)) - "Indentation retrieving variables matched to major modes. - -Which is used when `doom-modeline-indent-info' is non-nil. -When multiple variables are specified for a mode, they will be tried resolved -in the given order." - :type '(alist :key-type symbol :value-type sexp) - :group 'doom-modeline) - -(defcustom doom-modeline-vcs-icon t - "Whether display the icon of vcs segment. - -It respects option `doom-modeline-icon'." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-vcs-max-length 15 - "The maximum displayed length of the branch name of version control." - :type 'integer - :group 'doom-modeline) - -(defcustom doom-modeline-vcs-display-function #'doom-modeline-vcs-name - "The function to display the branch name." - :type 'function - :group 'doom-modeline) - -(defcustom doom-modeline-check-icon t - "Whether display the icon of check segment. - -It respects option `doom-modeline-icon'." - :type 'boolean - :group 'doom-modeline) - -(define-obsolete-variable-alias - 'doom-modeline-checker-simple-format - 'doom-modeline-check-simple-format - "4.2.0") - -(defcustom doom-modeline-check-simple-format nil - "If non-nil, only display one number for check information if applicable." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-number-limit 99 - "The maximum number displayed for notifications." - :type 'integer - :group 'doom-modeline) - -(defcustom doom-modeline-workspace-name t - "Whether display the workspace name. - -Non-nil to display in the mode-line." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-persp-name t - "Whether display the perspective name. - -Non-nil to display in the mode-line." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-display-default-persp-name nil - "If non nil the default perspective name is displayed in the mode-line." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-persp-icon t - "If non nil the perspective name is displayed alongside a folder icon." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-repl t - "Whether display the `repl' state. - -Non-nil to display in the mode-line." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-lsp t - "Whether display the `lsp' state. - -Non-nil to display in the mode-line." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-github nil - "Whether display the GitHub notifications. - -It requires `ghub' and `async' packages. Additionally, your GitHub personal -access token must have `notifications' permissions. - -If you use `pass' to manage your secrets, you also need to add this hook: - (add-hook \\='doom-modeline-before-github-fetch-notification-hook - #\\='auth-source-pass-enable)" - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-github-interval 1800 ; (* 30 60) - "The interval of checking GitHub." - :type 'integer - :group 'doom-modeline) - -(defcustom doom-modeline-env-version t - "Whether display the environment version." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-modal t - "Whether display the modal state. - -Including `evil', `overwrite', `god', `ryo' and `xah-fly-keys', etc." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-modal-icon t - "Whether display the modal state icon. - -Including `evil', `overwrite', `god', `ryo' and `xah-fly-keys', etc." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-modal-modern-icon t - "Whether display the modern icons for modals." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-always-show-macro-register nil - "When non-nil, always show the register name when recording an evil macro." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-mu4e nil - "Whether display the mu4e notifications. - -It requires `mu4e-alert' package." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-gnus nil - "Whether to display notifications from gnus. - -It requires `gnus' to be setup" - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-gnus-timer 2 - "The wait time in minutes before gnus fetches mail. - -If nil, don't set up a hook." - :type 'integer - :group 'doom-modeline) - -(defcustom doom-modeline-gnus-idle nil - "Whether to wait an idle time to scan for news. - -When t, sets `doom-modeline-gnus-timer' as an idle timer. If a -number, Emacs must have been idle this given time, checked after -reach the defined timer, to fetch news. The time step can be -configured in `gnus-demon-timestep'." - :type '(choice - (boolean :tag "Set `doom-modeline-gnus-timer' as an idle timer") - (number :tag "Set a custom idle timer")) - :group 'doom-modeline) - -(defcustom doom-modeline-gnus-excluded-groups nil - "A list of groups to be excluded from the unread count. -Groups' names list in `gnus-newsrc-alist'`" - :type '(repeat string) - :group 'doom-modeline) - -(defcustom doom-modeline-irc t - "Whether display the irc notifications. - -It requires `circe' or `erc' package." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-irc-buffers nil - "Whether display the unread irc buffers." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-irc-stylize #'doom-modeline-shorten-irc - "Function to stylize the irc buffer names." - :type 'function - :group 'doom-modeline) - -(defcustom doom-modeline-battery t - "Whether display the battery status. - -It respects `display-battery-mode'." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-time t - "Whether display the time. - -It respects `display-time-mode'." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-display-misc-in-all-mode-lines t - "Whether display the misc segment on all mode lines. - -If nil, display only if the mode line is active." - :type 'boolean - :group 'doom-modeline) - -(defcustom doom-modeline-always-visible-segments nil - "A list of segments that should be visible even in inactive windows." - :type '(repeat symbol) - :group 'doom-modeline) - -(defcustom doom-modeline-buffer-file-name-function #'identity - "The function to handle variable `buffer-file-name'." - :type 'function - :group 'doom-modeline) - -(defcustom doom-modeline-buffer-file-truename-function #'identity - "The function to handle `buffer-file-truename'." - :type 'function - :group 'doom-modeline) - -(defcustom doom-modeline-k8s-show-namespace t - "Whether to show the current Kubernetes context's default namespace." - :type 'boolean - :group 'doom-modeline) - - -;; -;; Faces -;; - -(defgroup doom-modeline-faces nil - "The faces of `doom-modeline'." - :group 'doom-modeline - :group 'faces - :link '(url-link :tag "Homepage" "https://github.com/seagle0128/doom-modeline")) - -(defface doom-modeline - '((t ())) - "Default face." - :group 'doom-modeline-faces) - -(defface doom-modeline-emphasis - '((t (:inherit (doom-modeline mode-line-emphasis)))) - "Face used for emphasis." - :group 'doom-modeline-faces) - -(defface doom-modeline-highlight - '((t (:inherit (doom-modeline mode-line-highlight)))) - "Face used for highlighting." - :group 'doom-modeline-faces) - -(defface doom-modeline-buffer-path - '((t (:inherit (doom-modeline-emphasis bold)))) - "Face used for the dirname part of the buffer path." - :group 'doom-modeline-faces) - -(defface doom-modeline-buffer-file - '((t (:inherit (doom-modeline mode-line-buffer-id bold)))) - "Face used for the filename part of the mode-line buffer path." - :group 'doom-modeline-faces) - -(defface doom-modeline-buffer-modified - '((t (:inherit (doom-modeline warning bold) :background unspecified))) - "Face used for the \\='unsaved\\=' symbol in the mode-line." - :group 'doom-modeline-faces) - -(defface doom-modeline-buffer-major-mode - '((t (:inherit (doom-modeline-emphasis bold)))) - "Face used for the major-mode segment in the mode-line." - :group 'doom-modeline-faces) - -(defface doom-modeline-buffer-minor-mode - '((t (:inherit (doom-modeline font-lock-doc-face) :weight normal :slant normal))) - "Face used for the minor-modes segment in the mode-line." - :group 'doom-modeline-faces) - -(defface doom-modeline-project-parent-dir - '((t (:inherit (doom-modeline font-lock-comment-face bold)))) - "Face used for the project parent directory of the mode-line buffer path." - :group 'doom-modeline-faces) - -(defface doom-modeline-project-dir - '((t (:inherit (doom-modeline font-lock-string-face bold)))) - "Face used for the project directory of the mode-line buffer path." - :group 'doom-modeline-faces) - -(defface doom-modeline-project-root-dir - '((t (:inherit (doom-modeline-emphasis bold)))) - "Face used for the project part of the mode-line buffer path." - :group 'doom-modeline-faces) - -(defface doom-modeline-panel - '((t (:inherit doom-modeline-highlight))) - "Face for \\='X out of Y\\=' segments. -This applies to `anzu', `evil-substitute', `iedit' etc." - :group 'doom-modeline-faces) - -(defface doom-modeline-host - '((t (:inherit (doom-modeline italic)))) - "Face for remote hosts in the mode-line." - :group 'doom-modeline-faces) - -(defface doom-modeline-input-method - '((t (:inherit (doom-modeline-emphasis)))) - "Face for input method in the mode-line." - :group 'doom-modeline-faces) - -(defface doom-modeline-input-method-alt - '((t (:inherit (doom-modeline font-lock-doc-face) :slant normal))) - "Alternative face for input method in the mode-line." - :group 'doom-modeline-faces) - -(defface doom-modeline-debug - '((t (:inherit (doom-modeline font-lock-doc-face) :slant normal))) - "Face for debug-level messages in the mode-line. Used by vcs, check, etc." - :group 'doom-modeline-faces) - -(defface doom-modeline-info - '((t (:inherit (doom-modeline success)))) - "Face for info-level messages in the mode-line. Used by vcs, check, etc." - :group 'doom-modeline-faces) - -(defface doom-modeline-warning - '((t (:inherit (doom-modeline warning)))) - "Face for warnings in the mode-line. Used by vcs, check, etc." - :group 'doom-modeline-faces) - -(defface doom-modeline-urgent - '((t (:inherit (doom-modeline error)))) - "Face for errors in the mode-line. Used by vcs, check, etc." - :group 'doom-modeline-faces) - -(defface doom-modeline-notification - '((t (:inherit doom-modeline-warning))) - "Face for notifications in the mode-line. Used by GitHub, mu4e, etc. -Also see the face `doom-modeline-unread-number'." - :group 'doom-modeline-faces) - -(defface doom-modeline-unread-number - '((t (:inherit doom-modeline :slant italic))) - "Face for unread number in the mode-line. Used by GitHub, mu4e, etc." - :group 'doom-modeline-faces) - -(defface doom-modeline-bar - '((t (:inherit doom-modeline-highlight))) - "The face used for the left-most bar in the mode-line of an active window." - :group 'doom-modeline-faces) - -(defface doom-modeline-bar-inactive - `((t (:inherit doom-modeline))) - "The face used for the left-most bar in the mode-line of an inactive window." - :group 'doom-modeline-faces) - -(defface doom-modeline-debug-visual - '((((background light)) :foreground "#D4843E" :inherit doom-modeline) - (((background dark)) :foreground "#915B2D" :inherit doom-modeline)) - "Face to use for the mode-line while debugging." - :group 'doom-modeline-faces) - -(defface doom-modeline-evil-emacs-state - '((t (:inherit (doom-modeline font-lock-builtin-face)))) - "Face for the Emacs state tag in evil indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-evil-insert-state - '((t (:inherit (doom-modeline font-lock-keyword-face)))) - "Face for the insert state tag in evil indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-evil-motion-state - '((t (:inherit (doom-modeline font-lock-doc-face) :slant normal))) - "Face for the motion state tag in evil indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-evil-normal-state - '((t (:inherit doom-modeline-info))) - "Face for the normal state tag in evil indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-evil-operator-state - '((t (:inherit (doom-modeline mode-line)))) - "Face for the operator state tag in evil indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-evil-visual-state - '((t (:inherit doom-modeline-warning))) - "Face for the visual state tag in evil indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-evil-replace-state - '((t (:inherit doom-modeline-urgent))) - "Face for the replace state tag in evil indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-evil-user-state - '((t (:inherit doom-modeline-warning))) - "Face for the replace state tag in evil indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-overwrite - '((t (:inherit doom-modeline-urgent))) - "Face for overwrite indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-god - '((t (:inherit doom-modeline-info))) - "Face for god-mode indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-ryo - '((t (:inherit doom-modeline-info))) - "Face for RYO indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-fly-insert-state - '((t (:inherit (doom-modeline font-lock-keyword-face)))) - "Face for the insert state in xah-fly-keys indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-fly-normal-state - '((t (:inherit doom-modeline-info))) - "Face for the normal state in xah-fly-keys indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-boon-command-state - '((t (:inherit doom-modeline-info))) - "Face for the command state tag in boon indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-boon-insert-state - '((t (:inherit (doom-modeline font-lock-keyword-face)))) - "Face for the insert state tag in boon indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-boon-special-state - '((t (:inherit (doom-modeline font-lock-builtin-face)))) - "Face for the special state tag in boon indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-boon-off-state - '((t (:inherit (doom-modeline mode-line)))) - "Face for the off state tag in boon indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-meow-normal-state - '((t (:inherit doom-modeline-evil-normal-state))) - "Face for the normal state in meow-edit indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-meow-insert-state - '((t (:inherit doom-modeline-evil-insert-state))) - "Face for the insert state in meow-edit indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-meow-beacon-state - '((t (:inherit doom-modeline-evil-visual-state))) - "Face for the beacon state in meow-edit indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-meow-motion-state - '((t (:inherit doom-modeline-evil-motion-state))) - "Face for the motion state in meow-edit indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-meow-keypad-state - '((t (:inherit doom-modeline-evil-operator-state))) - "Face for the keypad state in meow-edit indicator." - :group 'doom-modeline-faces) - -(defface doom-modeline-persp-name - '((t (:inherit (doom-modeline font-lock-comment-face italic)))) - "Face for the persp name." - :group 'doom-modeline-faces) - -(defface doom-modeline-persp-buffer-not-in-persp - '((t (:inherit (doom-modeline font-lock-doc-face italic)))) - "Face for the buffers which are not in the persp." - :group 'doom-modeline-faces) - -(defface doom-modeline-repl-success - '((t (:inherit doom-modeline-info))) - "Face for REPL success state." - :group 'doom-modeline-faces) - -(defface doom-modeline-repl-warning - '((t (:inherit doom-modeline-warning))) - "Face for REPL warning state." - :group 'doom-modeline-faces) - -(defface doom-modeline-lsp-success - '((t (:inherit doom-modeline-info))) - "Face for LSP success state." - :group 'doom-modeline-faces) - -(defface doom-modeline-lsp-warning - '((t (:inherit doom-modeline-warning))) - "Face for LSP warning state." - :group 'doom-modeline-faces) - -(defface doom-modeline-lsp-error - '((t (:inherit doom-modeline-urgent))) - "Face for LSP error state." - :group 'doom-modeline-faces) - -(defface doom-modeline-lsp-running - '((t (:inherit (doom-modeline compilation-mode-line-run) :weight normal :slant normal))) - "Face for LSP running state." - :group 'doom-modeline-faces) - -(defface doom-modeline-battery-charging - '((t (:inherit doom-modeline-info))) - "Face for battery charging status." - :group 'doom-modeline-faces) - -(defface doom-modeline-battery-full - '((t (:inherit doom-modeline-info))) - "Face for battery full status." - :group 'doom-modeline-faces) - -(defface doom-modeline-battery-normal - '((t (:inherit (doom-modeline mode-line)))) - "Face for battery normal status." - :group 'doom-modeline-faces) - -(defface doom-modeline-battery-warning - '((t (:inherit doom-modeline-warning))) - "Face for battery warning status." - :group 'doom-modeline-faces) - -(defface doom-modeline-battery-critical - '((t (:inherit doom-modeline-urgent))) - "Face for battery critical status." - :group 'doom-modeline-faces) - -(defface doom-modeline-battery-error - '((t (:inherit doom-modeline-urgent))) - "Face for battery error status." - :group 'doom-modeline-faces) - -(defface doom-modeline-buffer-timemachine - '((t (:inherit doom-modeline-buffer-file :slant italic))) - "Face for timemachine status." - :group 'doom-modeline-faces) - -(defface doom-modeline-time - '((t (:inherit doom-modeline))) - "Face for display time." - :group 'doom-modeline-faces) - -(defface doom-modeline-compilation - '((t (:inherit doom-modeline-warning :slant italic :height 0.9))) - "Face for compilation progress." - :group 'doom-modeline-faces) - -;; -;; Externals -;; - -(defvar mode-line-right-align-edge) - -(declare-function face-remap-remove-relative "face-remap") -(declare-function ffip-project-root "ext:find-file-in-project") -(declare-function project-root "project") -(declare-function projectile-project-root "ext:projectile") - - -;; -;; Utilities -;; - -(defun doom-modeline-add-font-lock () - "Fontify `doom-modeline-def-*' statements." - (font-lock-add-keywords - 'emacs-lisp-mode - '(("(\\(doom-modeline-def-.+\\)\\_> +\\(.*?\\)\\_>" - (1 font-lock-keyword-face) - (2 font-lock-constant-face))))) -(doom-modeline-add-font-lock) - -(defun doom-modeline-add-imenu () - "Add to `imenu' index." - (add-to-list - 'imenu-generic-expression - '("Modelines" - "^\\s-*(\\(doom-modeline-def-modeline\\)\\s-+\\(\\(?:\\sw\\|\\s_\\|\\s'\\|\\\\.\\)+\\)" - 2)) - (add-to-list - 'imenu-generic-expression - '("Segments" - "^\\s-*(\\(doom-modeline-def-segment\\)\\s-+\\(\\(?:\\sw\\|\\s_\\|\\\\.\\)+\\)" - 2)) - (add-to-list - 'imenu-generic-expression - '("Envs" - "^\\s-*(\\(doom-modeline-def-env\\)\\s-+\\(\\(?:\\sw\\|\\s_\\|\\\\.\\)+\\)" - 2))) - - -;; -;; Core helpers -;; - -;; FIXME #183: Force to calculate mode-line height -;; @see https://github.com/seagle0128/doom-modeline/issues/183 -;; @see https://github.com/seagle0128/doom-modeline/issues/483 -(unless (>= emacs-major-version 29) - (eval-and-compile - (defun doom-modeline-redisplay (&rest _) - "Call `redisplay' to trigger mode-line height calculations. - -Certain functions, including e.g. `fit-window-to-buffer', base -their size calculations on values which are incorrect if the -mode-line has a height different from that of the `default' face -and certain other calculations have not yet taken place for the -window in question. - -These calculations can be triggered by calling `redisplay' -explicitly at the appropriate time and this functions purpose -is to make it easier to do so. - -This function is like `redisplay' with non-nil FORCE argument, -but it will only trigger a redisplay when there is a non nil -`mode-line-format' and the height of the mode-line is different -from that of the `default' face. This function is intended to be -used as an advice to window creation functions." - (when (and (bound-and-true-p doom-modeline-mode) - mode-line-format - (/= (frame-char-height) (window-mode-line-height))) - (redisplay t)))) - (advice-add #'fit-window-to-buffer :before #'doom-modeline-redisplay)) - -;; For `flycheck-color-mode-line' -(with-eval-after-load 'flycheck-color-mode-line - (defvar flycheck-color-mode-line-face-to-color) - (setq flycheck-color-mode-line-face-to-color 'doom-modeline)) - -(defun doom-modeline-icon-displayable-p () - "Return non-nil if icons are displayable." - (and doom-modeline-icon (featurep 'nerd-icons))) - -(defun doom-modeline-mwheel-available-p () - "Whether mouse wheel is available." - (and (featurep 'mwheel) (bound-and-true-p mouse-wheel-mode))) - -;; Keep `doom-modeline-current-window' up-to-date -(defun doom-modeline--selected-window () - "Get the selected window." - (frame-selected-window)) - -(defvar doom-modeline-current-window (doom-modeline--selected-window) - "Current window.") - -(defun doom-modeline--active () - "Whether is an active window." - (unless (and (bound-and-true-p mini-frame-frame) - (and (frame-live-p mini-frame-frame) - (frame-visible-p mini-frame-frame))) - (and doom-modeline-current-window - (eq (doom-modeline--selected-window) doom-modeline-current-window)))) - -(defvar-local doom-modeline--limited-width-p nil) - -(defun doom-modeline--segment-visible (name) - "Whether the segment NAME should be displayed." - (and - (or (doom-modeline--active) - (member name doom-modeline-always-visible-segments)) - (not doom-modeline--limited-width-p))) - -(defun doom-modeline-set-selected-window (&rest _) - "Set `doom-modeline-current-window' appropriately." - (let ((win (doom-modeline--selected-window))) - (setq doom-modeline-current-window - (if (minibuffer-window-active-p win) - (minibuffer-selected-window) - win)))) - -(defun doom-modeline-unset-selected-window () - "Unset `doom-modeline-current-window' appropriately." - (setq doom-modeline-current-window nil)) - -(add-hook 'pre-redisplay-functions #'doom-modeline-set-selected-window) - -;; Ensure modeline is inactive when Emacs is unfocused -(defvar doom-modeline--remap-faces '(mode-line - mode-line-active - mode-line-emphasis - mode-line-highlight - mode-line-buffer-id - doom-modeline - solaire-mode-line-face - solaire-mode-line-active-face - paradox-mode-line-face - flycheck-color-mode-line-error-face - flycheck-color-mode-line-warning-face - flycheck-color-mode-line-info-face - flycheck-color-mode-line-success-face)) - -(defvar doom-modeline--remap-face-cookie-alist nil) -(defun doom-modeline-focus () - "Focus mode-line." - (mapc #'face-remap-remove-relative doom-modeline--remap-face-cookie-alist)) - -(defun doom-modeline-unfocus () - "Unfocus mode-line." - (dolist (face doom-modeline--remap-faces) - (add-to-list 'doom-modeline--remap-face-cookie-alist - (face-remap-add-relative face 'mode-line-inactive)))) - -(with-no-warnings - (if (boundp 'after-focus-change-function) - (progn - (defun doom-modeline-focus-change (&rest _) - (if (frame-focus-state (frame-parent)) - (progn - (doom-modeline-focus) - ;; HACK: pulse after focusing in the frame to refresh the buffer name. - ;; @see https://github.com/seagle0128/doom-modeline/issues/591 - (when (fboundp 'pulse-momentary-highlight-region) - (pulse-momentary-highlight-region 0 0))) - (doom-modeline-unfocus))) - (advice-add #'handle-switch-frame :after #'doom-modeline-focus-change) - (add-function :after after-focus-change-function #'doom-modeline-focus-change)) - (progn - (add-hook 'focus-in-hook #'doom-modeline-focus) - (add-hook 'focus-out-hook #'doom-modeline-unfocus)))) - - -;; -;; Core -;; - -(defvar doom-modeline-fn-alist ()) -(defvar doom-modeline-var-alist ()) - -(defmacro doom-modeline-def-segment (name &rest body) - "Define a modeline segment NAME with BODY and byte compiles it." - (declare (indent defun) (doc-string 2)) - (let ((sym (intern (format "doom-modeline-segment--%s" name))) - (docstring (if (stringp (car body)) - (pop body) - (format "%s modeline segment" name)))) - (cond ((and (symbolp (car body)) - (not (cdr body))) - `(add-to-list 'doom-modeline-var-alist (cons ',name ',(car body)))) - (t - `(progn - (defun ,sym () ,docstring ,@body) - (add-to-list 'doom-modeline-fn-alist (cons ',name ',sym)) - ,(unless (bound-and-true-p byte-compile-current-file) - `(let (byte-compile-warnings) - (unless (and (fboundp 'subr-native-elisp-p) - (subr-native-elisp-p (symbol-function #',sym))) - (byte-compile #',sym))))))))) - -(defun doom-modeline--prepare-segments (segments) - "Prepare mode-line `SEGMENTS'." - (let (forms it) - (dolist (seg segments) - (cond ((stringp seg) - (push seg forms)) - ((symbolp seg) - (cond ((setq it (alist-get seg doom-modeline-fn-alist)) - (push (list :eval (list it)) forms)) - ((setq it (alist-get seg doom-modeline-var-alist)) - (push it forms)) - ((error "%s is not a defined segment" seg)))) - ((error "%s is not a valid segment" seg)))) - (nreverse forms))) - -(defun doom-modeline-def-modeline (name lhs &optional rhs) - "Define a modeline format and byte-compiles it. -NAME is a symbol to identify it (used by `doom-modeline' for retrieval). -LHS and RHS are lists of symbols of modeline segments defined with -`doom-modeline-def-segment'. - -Example: - (doom-modeline-def-modeline \\='minimal - \\='(bar matches \" \" buffer-info) - \\='(media-info major-mode)) - (doom-modeline-set-modeline \\='minimal t)" - (let ((sym (intern (format "doom-modeline-format--%s" name))) - (lhs-forms (doom-modeline--prepare-segments lhs)) - (rhs-forms (doom-modeline--prepare-segments rhs))) - (defalias sym - (lambda () - (list lhs-forms - (let* ((rhs-str (format-mode-line (cons "" rhs-forms))) - (rhs-width (progn - (add-face-text-property - 0 (length rhs-str) 'mode-line t rhs-str) - (doom-modeline-string-pixel-width rhs-str)))) - (propertize - " " - 'face (doom-modeline-face) - 'display - ;; Backport from `mode-line-right-align-edge' in 30 - (if (and (display-graphic-p) - (not (eq mode-line-right-align-edge 'window))) - `(space :align-to (- ,mode-line-right-align-edge - (,rhs-width))) - `(space :align-to (,(- (window-pixel-width) - (window-scroll-bar-width) - (window-right-divider-width) - (* (or (cdr (window-margins)) 1) - (frame-char-width)) - (pcase mode-line-right-align-edge - ('right-margin - (or (cdr (window-margins)) 0)) - ('right-fringe - (or (cadr (window-fringes)) 0)) - (_ 0)) - rhs-width)))))) - rhs-forms)) - (concat "Modeline:\n" - (format " %s\n %s" - (prin1-to-string lhs) - (prin1-to-string rhs)))))) -(put 'doom-modeline-def-modeline 'lisp-indent-function 'defun) - -(defun doom-modeline (key) - "Return a mode-line configuration associated with KEY (a symbol). -Throws an error if it doesn't exist." - (let ((fn (intern-soft (format "doom-modeline-format--%s" key)))) - (when (functionp fn) - `(:eval (,fn))))) - -(defun doom-modeline-set-modeline (key &optional default) - "Set the modeline format. Does nothing if the modeline KEY doesn't exist. -If DEFAULT is non-nil, set the default mode-line for all buffers." - (when-let ((modeline (doom-modeline key))) - (setf (if default - (default-value 'mode-line-format) - mode-line-format) - (list "%e" modeline)))) - -;; -;; Helpers -;; - -(defconst doom-modeline-ellipsis - (if (char-displayable-p ?…) "…" "...") - "Ellipsis.") - -(defsubst doom-modeline-spc () - "Whitespace." - (propertize " " 'face (doom-modeline-face))) - -(defsubst doom-modeline-wspc () - "Wide Whitespace." - (propertize " " 'face (doom-modeline-face))) - -(defsubst doom-modeline-vspc () - "Thin whitespace." - (propertize " " - 'face (doom-modeline-face) - 'display '((space :relative-width 0.5)))) - -(defun doom-modeline-face (&optional face inactive-face) - "Display FACE in active window, and INACTIVE-FACE in inactive window. -IF FACE is nil, `mode-line' face will be used. -If INACTIVE-FACE is nil, `mode-line-inactive' face will be used." - (if (doom-modeline--active) - (or (and (facep face) `(:inherit (doom-modeline ,face))) - (and (facep 'mode-line-active) '(:inherit (doom-modeline mode-line-active))) - '(:inherit (doom-modeline mode-line))) - (or (and (facep face) `(:inherit (doom-modeline mode-line-inactive ,face))) - (and (facep inactive-face) `(:inherit (doom-modeline ,inactive-face))) - '(:inherit (doom-modeline mode-line-inactive))))) - -(defun doom-modeline-string-pixel-width (str) - "Return the width of STR in pixels." - (if (fboundp 'string-pixel-width) - (string-pixel-width str) - (* (string-width str) (window-font-width nil 'mode-line) - (if (display-graphic-p) 1.05 1.0)))) - -(defun doom-modeline--font-height () - "Calculate the actual char height of the mode-line." - (let ((height (face-attribute 'mode-line :height)) - (char-height (window-font-height nil 'mode-line))) - (round - (* 1.0 (cond ((integerp height) (/ height 10)) - ((floatp height) (* height char-height)) - (t char-height)))))) - -(defun doom-modeline--original-value (sym) - "Return the original value for SYM, if any. - -If SYM has an original value, return it in a list. Return nil -otherwise." - (let* ((orig-val-expr (get sym 'standard-value))) - (when (consp orig-val-expr) - (ignore-errors - (list - (eval (car orig-val-expr))))))) - -(defun doom-modeline-add-variable-watcher (symbol watch-function) - "Cause WATCH-FUNCTION to be called when SYMBOL is set if possible. - -See docs of `add-variable-watcher'." - (when (fboundp 'add-variable-watcher) - (add-variable-watcher symbol watch-function))) - -(defun doom-modeline-propertize-icon (icon &optional face) - "Propertize the ICON with the specified FACE. - -The face should be the first attribute, or the font family may be overridden. -So convert the face \":family XXX :height XXX :inherit XXX\" to -\":inherit XXX :family XXX :height XXX\". -See https://github.com/seagle0128/doom-modeline/issues/301." - (when icon - (if (doom-modeline-icon-displayable-p) - (when-let ((props (get-text-property 0 'face icon))) - (when (listp props) - (cl-destructuring-bind (&key family height inherit &allow-other-keys) props - (propertize icon 'face `(:inherit (doom-modeline ,(or face inherit props)) - :family ,(or family "") - :height ,(or height 1.0)))))) - (propertize icon 'face `(:inherit (doom-modeline ,face)))))) - -(defun doom-modeline-icon (icon-set icon-name unicode text &rest args) - "Display icon of ICON-NAME with ARGS in mode-line. - -ICON-SET includes `ipsicon', `octicon', `pomicon', `powerline', `faicon', -`wicon', `sucicon', `devicon', `codicon', `flicon' and `mdicon', etc. -UNICODE is the unicode char fallback. TEXT is the ASCII char fallback. -ARGS is same as `nerd-icons-octicon' and others." - (let ((face `(:inherit (doom-modeline - ,(or (plist-get args :face) 'mode-line))))) - (cond - ;; Icon - ((and (doom-modeline-icon-displayable-p) - icon-name - (not (string-empty-p icon-name))) - (if-let* ((func (nerd-icons--function-name icon-set)) - (icon (and (fboundp func) - (apply func icon-name args)))) - (doom-modeline-propertize-icon icon face) - "")) - ;; Unicode fallback - ((and doom-modeline-unicode-fallback - unicode - (not (string-empty-p unicode)) - (char-displayable-p (string-to-char unicode))) - (propertize unicode 'face face)) - ;; ASCII text - (text - (propertize text 'face face)) - ;; Fallback - (t "")))) - -(defun doom-modeline-icon-for-buffer () - "Get the formatted icon for the current buffer." - (nerd-icons-icon-for-buffer)) - -(defun doom-modeline-display-icon (icon) - "Display ICON in mode-line." - (if (doom-modeline--active) - icon - (doom-modeline-propertize-icon icon 'mode-line-inactive))) - -(defun doom-modeline-display-text (text) - "Display TEXT in mode-line." - (if (doom-modeline--active) - text - (propertize text 'face `(:inherit (mode-line-inactive - ,(get-text-property 0 'face text)))))) - -(defun doom-modeline-vcs-name () - "Display the vcs name." - (and vc-mode (cadr (split-string (string-trim vc-mode) "^[A-Z]+[-:]+")))) - -(defun doom-modeline--create-bar-image (face width height) - "Create the bar image. - -Use FACE for the bar, WIDTH and HEIGHT are the image size in pixels." - (when (and (image-type-available-p 'pbm) - (numberp width) (> width 0) - (numberp height) (> height 0)) - (propertize - " " 'display - (let ((color (or (face-background face nil t) "None"))) - (ignore-errors - (create-image - (concat (format "P1\n%i %i\n" width height) - (make-string (* width height) ?1) - "\n") - 'pbm t :scale 1 :foreground color :ascent 'center)))))) - -(defun doom-modeline--create-hud-image - (face1 face2 width height top-margin bottom-margin) - "Create the hud image. - -Use FACE1 for the bar, FACE2 for the background. -WIDTH and HEIGHT are the image size in pixels. -TOP-MARGIN and BOTTOM-MARGIN are the size of the margin above and below the bar, -respectively." - (when (and (display-graphic-p) - (image-type-available-p 'pbm) - (numberp width) (> width 0) - (numberp height) (> height 0)) - (let ((min-height (min height doom-modeline-hud-min-height))) - (unless (> (- height top-margin bottom-margin) min-height) - (let ((margin (- height min-height))) - (setq top-margin (/ (* margin top-margin) (+ top-margin bottom-margin)) - bottom-margin (- margin top-margin))))) - (propertize - " " 'display - (let ((color1 (or (face-background face1 nil t) "None")) - (color2 (or (face-background face2 nil t) "None"))) - (create-image - (concat - (format "P1\n%i %i\n" width height) - (make-string (* top-margin width) ?0) - (make-string (* (- height top-margin bottom-margin) width) ?1) - (make-string (* bottom-margin width) ?0) - "\n") - 'pbm t :foreground color1 :background color2 :ascent 'center))))) - -;; Check whether `window-total-width' is smaller than the limit -(defun doom-modeline-window-size-change-function (&rest _) - "Function for `window-size-change-functions'." - (setq doom-modeline--limited-width-p - (cond - ((integerp doom-modeline-window-width-limit) - (<= (window-total-width) doom-modeline-window-width-limit)) - ((floatp doom-modeline-window-width-limit) - (<= (/ (window-total-width) (frame-width) 1.0) - doom-modeline-window-width-limit))))) - -(add-hook 'after-revert-hook #'doom-modeline-window-size-change-function) -(add-hook 'buffer-list-update-hook #'doom-modeline-window-size-change-function) -(add-hook 'window-size-change-functions #'doom-modeline-window-size-change-function) - -(defvar-local doom-modeline--project-root nil) -(defun doom-modeline--project-root () - "Get the path to the project root. -Return nil if no project was found." - (or doom-modeline--project-root - (setq doom-modeline--project-root - (cond - ((and (memq doom-modeline-project-detection '(auto ffip)) - (fboundp 'ffip-project-root)) - (let ((inhibit-message t)) - (ffip-project-root))) - ((and (memq doom-modeline-project-detection '(auto projectile)) - (bound-and-true-p projectile-mode)) - (projectile-project-root)) - ((and (memq doom-modeline-project-detection '(auto project)) - (fboundp 'project-current)) - (when-let ((project (project-current))) - (expand-file-name - (if (fboundp 'project-root) - (project-root project) - (car (with-no-warnings - (project-roots project))))))))))) - -(doom-modeline-add-variable-watcher - 'doom-modeline-project-detection - (lambda (_sym val op _where) - (when (eq op 'set) - (setq doom-modeline-project-detection val) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (setq doom-modeline--project-root nil) - (and buffer-file-name (revert-buffer t t))))))) - -(defun doom-modeline-project-p () - "Check if the file is in a project." - (doom-modeline--project-root)) - -(defun doom-modeline-project-root () - "Get the path to the root of your project. -Return `default-directory' if no project was found." - (abbreviate-file-name - (or (doom-modeline--project-root) default-directory))) - -(defun doom-modeline--format-buffer-file-name () - "Get and format the buffer file name." - (let ((buffer-file-name (file-local-name - (or (buffer-file-name (buffer-base-buffer)) "")))) - (or (and doom-modeline-buffer-file-name-function - (funcall doom-modeline-buffer-file-name-function buffer-file-name)) - buffer-file-name))) - -(defun doom-modeline--format-buffer-file-truename (b-f-n) - "Get and format buffer file truename via B-F-N." - (let ((buffer-file-truename (file-local-name - (or (file-truename b-f-n) "")))) - (or (and doom-modeline-buffer-file-truename-function - (funcall doom-modeline-buffer-file-truename-function buffer-file-truename)) - buffer-file-truename))) - -(defun doom-modeline-buffer-file-name () - "Propertize file name based on `doom-modeline-buffer-file-name-style'." - (let* ((buffer-file-name (doom-modeline--format-buffer-file-name)) - (buffer-file-truename (doom-modeline--format-buffer-file-truename buffer-file-name)) - (file-name - (pcase doom-modeline-buffer-file-name-style - ('auto - (if (doom-modeline-project-p) - (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink 'shrink 'hide) - (propertize "%b" 'face 'doom-modeline-buffer-file))) - ('truncate-upto-project - (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink)) - ('truncate-from-project - (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename nil 'shrink)) - ('truncate-with-project - (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink 'shrink 'hide)) - ('truncate-except-project - (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink 'shrink)) - ('truncate-upto-root - (doom-modeline--buffer-file-name-truncate buffer-file-name buffer-file-truename)) - ('truncate-all - (doom-modeline--buffer-file-name-truncate buffer-file-name buffer-file-truename t)) - ('truncate-nil - (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename)) - ('relative-to-project - (doom-modeline--buffer-file-name-relative buffer-file-name buffer-file-truename)) - ('relative-from-project - (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename nil nil 'hide)) - ('file-name - (propertize (file-name-nondirectory buffer-file-name) - 'face 'doom-modeline-buffer-file)) - ('file-name-with-project - (format "%s|%s" - (propertize (file-name-nondirectory - (directory-file-name (file-local-name (doom-modeline-project-root)))) - 'face 'doom-modeline-project-dir) - (propertize (file-name-nondirectory buffer-file-name) - 'face 'doom-modeline-buffer-file))) - ((or 'buffer-name _) - (propertize "%b" 'face 'doom-modeline-buffer-file))))) - (propertize (if (string-empty-p file-name) - (propertize "%b" 'face 'doom-modeline-buffer-file) - file-name) - 'mouse-face 'mode-line-highlight - 'help-echo (concat buffer-file-truename - (unless (string= (file-name-nondirectory buffer-file-truename) - (buffer-name)) - (concat "\n" (buffer-name))) - "\nmouse-1: Previous buffer\nmouse-3: Next buffer") - 'local-map mode-line-buffer-identification-keymap))) - -(defun doom-modeline--buffer-file-name-truncate (file-path true-file-path &optional truncate-tail) - "Propertize file name that truncates every dir along path. - -If TRUNCATE-TAIL is t also truncate the parent directory of the file." - (let ((dirs (shrink-path-prompt (file-name-directory true-file-path)))) - (if (null dirs) - (propertize "%b" 'face 'doom-modeline-buffer-file) - (let ((dirname (car dirs)) - (basename (cdr dirs))) - (concat (propertize (concat dirname - (if truncate-tail (substring basename 0 1) basename) - "/") - 'face 'doom-modeline-project-root-dir) - (propertize (file-name-nondirectory file-path) - 'face 'doom-modeline-buffer-file)))))) - -(defun doom-modeline--buffer-file-name-relative (_file-path true-file-path &optional include-project) - "Propertize file name showing directories relative to project's root only. - -If INCLUDE-PROJECT is non-nil, the project path will be included." - (let ((root (file-local-name (doom-modeline-project-root)))) - (if (null root) - (propertize "%b" 'face 'doom-modeline-buffer-file) - (let ((relative-dirs (file-relative-name (file-name-directory true-file-path) - (if include-project (concat root "../") root)))) - (and (equal "./" relative-dirs) (setq relative-dirs "")) - (concat (propertize relative-dirs 'face 'doom-modeline-buffer-path) - (propertize (file-name-nondirectory true-file-path) - 'face 'doom-modeline-buffer-file)))))) - -(defun doom-modeline--buffer-file-name (file-path - true-file-path - &optional - truncate-project-root-parent - truncate-project-relative-path - hide-project-root-parent) - "Propertize buffer name given by FILE-PATH or TRUE-FILE-PATH. - -If TRUNCATE-PROJECT-ROOT-PARENT is non-nil will be saved by truncating project -root parent down fish-shell style. - -Example: - ~/Projects/FOSS/emacs/lisp/comint.el => ~/P/F/emacs/lisp/comint.el - -If TRUNCATE-PROJECT-RELATIVE-PATH is non-nil will be saved by truncating project -relative path down fish-shell style. - -Example: - ~/Projects/FOSS/emacs/lisp/comint.el => ~/Projects/FOSS/emacs/l/comint.el - -If HIDE-PROJECT-ROOT-PARENT is non-nil will hide project root parent. - -Example: - ~/Projects/FOSS/emacs/lisp/comint.el => emacs/lisp/comint.el" - (let ((project-root (file-local-name (doom-modeline-project-root)))) - (concat - ;; Project root parent - (unless hide-project-root-parent - (when-let (root-path-parent - (file-name-directory (directory-file-name project-root))) - (propertize - (if (and truncate-project-root-parent - (not (string-empty-p root-path-parent)) - (not (string= root-path-parent "/"))) - (shrink-path--dirs-internal root-path-parent t) - (abbreviate-file-name root-path-parent)) - 'face 'doom-modeline-project-parent-dir))) - ;; Project directory - (propertize - (concat (file-name-nondirectory (directory-file-name project-root)) "/") - 'face 'doom-modeline-project-dir) - ;; relative path - (propertize - (when-let (relative-path (file-relative-name - (or (file-name-directory - (if doom-modeline-buffer-file-true-name - true-file-path file-path)) - "./") - project-root)) - (if (string= relative-path "./") - "" - (if truncate-project-relative-path - (substring (shrink-path--dirs-internal relative-path t) 1) - relative-path))) - 'face 'doom-modeline-buffer-path) - ;; File name - (propertize (file-name-nondirectory file-path) - 'face 'doom-modeline-buffer-file)))) - -(provide 'doom-modeline-core) - -;;; doom-modeline-core.el ends here diff --git a/emacs/elpa/doom-modeline-20240810.1702/doom-modeline-pkg.el b/emacs/elpa/doom-modeline-20240810.1702/doom-modeline-pkg.el @@ -1,17 +0,0 @@ -(define-package "doom-modeline" "20240810.1702" "A minimal and modern mode-line" - '((emacs "25.1") - (compat "29.1.4.5") - (nerd-icons "0.1.0") - (shrink-path "0.3.1")) - :commit "35215cbe0293e9404ab2ce96177a995f9623832a" :authors - '(("Vincent Zhang" . "seagle0128@gmail.com")) - :maintainers - '(("Vincent Zhang" . "seagle0128@gmail.com")) - :maintainer - '("Vincent Zhang" . "seagle0128@gmail.com") - :keywords - '("faces" "mode-line") - :url "https://github.com/seagle0128/doom-modeline") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/doom-modeline-20240810.1702/doom-modeline-segments.elc b/emacs/elpa/doom-modeline-20240810.1702/doom-modeline-segments.elc Binary files differ. diff --git a/emacs/elpa/doom-modeline-20240810.1702/doom-modeline-autoloads.el b/emacs/elpa/doom-modeline-20240811.1437/doom-modeline-autoloads.el diff --git a/emacs/elpa/doom-modeline-20240811.1437/doom-modeline-core.el b/emacs/elpa/doom-modeline-20240811.1437/doom-modeline-core.el @@ -0,0 +1,1705 @@ +;;; doom-modeline-core.el --- The core libraries for doom-modeline -*- lexical-binding: t; -*- + +;; Copyright (C) 2018-2024 Vincent Zhang + +;; This file is not part of GNU Emacs. + +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. +;; + +;;; Commentary: +;; +;; The core libraries for doom-modeline. +;; + +;;; Code: + +(require 'compat) +(eval-when-compile + (require 'cl-lib) + (require 'subr-x)) +(require 'nerd-icons) +(require 'shrink-path) + + +;; +;; Compatibility +;; + +(unless (boundp 'mode-line-right-align-edge) + (defcustom mode-line-right-align-edge 'window + "Where mode-line should align to. +Internally, that function uses `:align-to' in a display property, +so aligns to the left edge of the given area. See info node +`(elisp)Pixel Specification'. + +Must be set to a symbol. Acceptable values are: +- `window': align to extreme right of window, regardless of margins + or fringes +- `right-fringe': align to right-fringe +- `right-margin': align to right-margin" + :type '(choice (const right-margin) + (const right-fringe) + (const window)) + :group 'mode-line)) + + +;; +;; Optimization +;; + +;; Don’t compact font caches during GC. +(when (eq system-type 'windows-nt) + (setq inhibit-compacting-font-caches t)) + + +;; +;; Customization +;; + +(defgroup doom-modeline nil + "A minimal and modern mode-line." + :group 'mode-line + :link '(url-link :tag "Homepage" "https://github.com/seagle0128/doom-modeline")) + +(defcustom doom-modeline-support-imenu nil + "If non-nil, cause imenu to see `doom-modeline' declarations. +This is done by adjusting `lisp-imenu-generic-expression' to +include support for finding `doom-modeline-def-*' forms. + +Must be set before loading `doom-modeline'." + :type 'boolean + :set (lambda (_sym val) + (if val + (add-hook 'emacs-lisp-mode-hook #'doom-modeline-add-imenu) + (remove-hook 'emacs-lisp-mode-hook #'doom-modeline-add-imenu))) + :group 'doom-modeline) + +(defcustom doom-modeline-height (+ (frame-char-height) 4) + "How tall the mode-line should be. It's only respected in GUI. +If the actual char height is larger, it respects the actual char height." + :type 'integer + :group 'doom-modeline) + +(defcustom doom-modeline-bar-width 4 + "How wide the mode-line bar should be. It's only respected in GUI." + :type 'integer + :set (lambda (sym val) + (set sym (if (> val 0) val 1))) + :group 'doom-modeline) + +(defcustom doom-modeline-hud nil + "Whether to use hud instead of default bar. It's only respected in GUI." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-hud-min-height 2 + "Minimum height in pixels of the \"thumb\" of the hud. +Only respected in GUI." + :type 'integer + :set (lambda (sym val) + (set sym (if (> val 1) val 1))) + :group 'doom-modeline) + +(defcustom doom-modeline-window-width-limit 85 + "The limit of the window width. + +If `window-width' is smaller than the limit, some information won't be +displayed. It can be an integer or a float number. nil means no limit." + :type '(choice integer + float + (const :tag "Disable" nil)) + :group 'doom-modeline) + +(defcustom doom-modeline-project-detection 'auto + "How to detect the project root. + +nil means to use `default-directory'. + +The project management packages have some issues on detecting project root. +e.g. `projectile' doesn't handle symlink folders well, while `project' is +unable to handle sub-projects. +Specify another one if you encounter the issue." + :type '(choice (const :tag "Auto-detect" auto) + (const :tag "Find File in Project" ffip) + (const :tag "Projectile" projectile) + (const :tag "Built-in Project" project) + (const :tag "Disable" nil)) + :group 'doom-modeline) + +(defcustom doom-modeline-buffer-file-name-style 'auto + "Determines the style used by `doom-modeline-buffer-file-name'. + +Given ~/Projects/FOSS/emacs/lisp/comint.el + auto => emacs/l/comint.el (in a project) or comint.el + truncate-upto-project => ~/P/F/emacs/lisp/comint.el + truncate-from-project => ~/Projects/FOSS/emacs/l/comint.el + truncate-with-project => emacs/l/comint.el + truncate-except-project => ~/P/F/emacs/l/comint.el + truncate-upto-root => ~/P/F/e/lisp/comint.el + truncate-all => ~/P/F/e/l/comint.el + truncate-nil => ~/Projects/FOSS/emacs/lisp/comint.el + relative-from-project => emacs/lisp/comint.el + relative-to-project => lisp/comint.el + file-name => comint.el + file-name-with-project => FOSS|comint.el + buffer-name => comint.el<2> (uniquify buffer name)" + :type '(choice (const auto) + (const truncate-upto-project) + (const truncate-from-project) + (const truncate-with-project) + (const truncate-except-project) + (const truncate-upto-root) + (const truncate-all) + (const truncate-nil) + (const relative-from-project) + (const relative-to-project) + (const file-name) + (const file-name-with-project) + (const buffer-name)) + :group'doom-modeline) + +(defcustom doom-modeline-buffer-file-true-name nil + "Use `file-truename' on buffer file name. + +Project detection(projectile.el) may uses `file-truename' on directory path. +Turn on this to provide right relative path for buffer file name." + :type 'boolean + :group'doom-modeline) + +(defcustom doom-modeline-icon t + "Whether display the icons in the mode-line. + +While using the server mode in GUI, should set the value explicitly." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-major-mode-icon t + "Whether display the icon for `major-mode'. + +It respects option `doom-modeline-icon'." + :type 'boolean + :group'doom-modeline) + +(defcustom doom-modeline-major-mode-color-icon t + "Whether display the colorful icon for `major-mode'. + +It respects option `nerd-icons-color-icons'." + :type 'boolean + :group'doom-modeline) + +(defcustom doom-modeline-buffer-state-icon t + "Whether display the icon for the buffer state. + +It respects option `doom-modeline-icon'." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-buffer-modification-icon t + "Whether display the modification icon for the buffer. + +It respects option `doom-modeline-icon' and `doom-modeline-buffer-state-icon'." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-lsp-icon t + "Whether display the icon of lsp client. + +It respects option `doom-modeline-icon'." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-time-icon t + "Whether display the icon of time. + +It respects option `doom-modeline-icon'." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-time-live-icon t + "Whether display the live icons of time. + +It respects option `doom-modeline-icon' and option `doom-modeline-time-icon'." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-time-analogue-clock t + "Whether to draw an analogue clock SVG as the live time icon. +It respects the option `doom-modeline-icon', option `doom-modeline-time-icon', +and option `doom-modeline-time-live-icon'." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-time-clock-minute-resolution 1 + "The clock will be updated every this many minutes, truncated. +See `doom-modeline-time-analogue-clock'." + :type 'natnum + :group 'doom-modeline) + +(defcustom doom-modeline-time-clock-size 0.7 + "Size of the analogue clock drawn, either in pixels or as a proportional height. +An integer value is used as the diameter of clock in pixels. +A floating point value sets the diameter of the clock realtive to +`doom-modeline-height'. + +Only relevant when `doom-modeline-time-analogue-clock' is non-nil, which see." + :type 'number + :group 'doom-modeline) + +(defcustom doom-modeline-unicode-fallback nil + "Whether to use unicode as a fallback (instead of ASCII) when not using icons." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-buffer-name t + "Whether display the buffer name." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-highlight-modified-buffer-name t + "Whether highlight the modified buffer name." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-column-zero-based t + "When non-nil, mode line displays column numbers zero-based. +See `column-number-indicator-zero-based'." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-percent-position '(-3 "%p") + "Specification of \"percentage offset\" of window through buffer. +See `mode-line-percent-position'." + :type '(radio + (const :tag "nil: No offset is displayed" nil) + (const :tag "\"%o\": Proportion of \"travel\" of the window through the buffer" + (-3 "%o")) + (const :tag "\"%p\": Percentage offset of top of window" + (-3 "%p")) + (const :tag "\"%P\": Percentage offset of bottom of window" + (-3 "%P")) + (const :tag "\"%q\": Offsets of both top and bottom of window" + (6 "%q"))) + :group 'doom-modeline) + +(defcustom doom-modeline-position-line-format '("L%l") + "Format used to display line numbers in the mode line. +See `mode-line-position-line-format'." + :type '(list string) + :group 'doom-modeline) + +(defcustom doom-modeline-position-column-format '("C%c") + "Format used to display column numbers in the mode line. +See `mode-line-position-column-format'." + :type '(list string) + :group 'doom-modeline) + +(defcustom doom-modeline-position-column-line-format '("%l:%c") + "Format used to display combined line/column numbers in the mode line. +See `mode-line-position-column-line-format'." + :type '(list string) + :group 'doom-modeline) + +(defcustom doom-modeline-minor-modes nil + "Whether display the minor modes in the mode-line." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-enable-word-count nil + "If non-nil, a word count will be added to the selection-info modeline segment." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-continuous-word-count-modes + '(markdown-mode gfm-mode org-mode) + "Major modes in which to display word count continuously. + +It respects `doom-modeline-enable-word-count'." + :type '(repeat (symbol :tag "Major-Mode") ) + :group 'doom-modeline) + +(defcustom doom-modeline-buffer-encoding t + "Whether display the buffer encoding." + :type '(choice (const :tag "Always" t) + (const :tag "When non-default" nondefault) + (const :tag "Never" nil)) + :group 'doom-modeline) + +(defcustom doom-modeline-default-coding-system 'utf-8 + "Default coding system for `doom-modeline-buffer-encoding' `nondefault'." + :type 'coding-system + :group 'doom-modeline) + +(defcustom doom-modeline-default-eol-type 0 + "Default EOL type for `doom-modeline-buffer-encoding' `nondefault'." + :type '(choice (const :tag "Unix-style LF" 0) + (const :tag "DOS-style CRLF" 1) + (const :tag "Mac-style CR" 2)) + :group 'doom-modeline) + +(defcustom doom-modeline-indent-info nil + "Whether display the indentation information." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-total-line-number nil + "Whether display the total line number." + :type 'boolean + :group 'doom-modeline) + +;; It is based upon `editorconfig-indentation-alist' but is used to read indentation levels instead +;; of setting them. (https://github.com/editorconfig/editorconfig-emacs) +(defcustom doom-modeline-indent-alist + '((apache-mode apache-indent-level) + (awk-mode c-basic-offset) + (bpftrace-mode c-basic-offset) + (c++-mode c-basic-offset) + (c-mode c-basic-offset) + (cmake-mode cmake-tab-width) + (coffee-mode coffee-tab-width) + (cperl-mode cperl-indent-level) + (crystal-mode crystal-indent-level) + (csharp-mode c-basic-offset) + (css-mode css-indent-offset) + (d-mode c-basic-offset) + (emacs-lisp-mode lisp-indent-offset) + (enh-ruby-mode enh-ruby-indent-level) + (erlang-mode erlang-indent-level) + (ess-mode ess-indent-offset) + (f90-mode f90-associate-indent + f90-continuation-indent + f90-critical-indent + f90-do-indent + f90-if-indent + f90-program-indent + f90-type-indent) + (feature-mode feature-indent-offset + feature-indent-level) + (fsharp-mode fsharp-continuation-offset + fsharp-indent-level + fsharp-indent-offset) + (groovy-mode groovy-indent-offset) + (haskell-mode haskell-indent-spaces + haskell-indent-offset + haskell-indentation-layout-offset + haskell-indentation-left-offset + haskell-indentation-starter-offset + haskell-indentation-where-post-offset + haskell-indentation-where-pre-offset + shm-indent-spaces) + (haxor-mode haxor-tab-width) + (idl-mode c-basic-offset) + (jade-mode jade-tab-width) + (java-mode c-basic-offset) + (js-mode js-indent-level) + (js-jsx-mode js-indent-level + sgml-basic-offset) + (js2-mode js2-basic-offset) + (js2-jsx-mode js2-basic-offset + sgml-basic-offset) + (js3-mode js3-indent-level) + (json-mode js-indent-level) + (julia-mode julia-indent-offset) + (kotlin-mode kotlin-tab-width) + (latex-mode tex-indent-basic) + (lisp-mode lisp-indent-offset) + (livescript-mode livescript-tab-width) + (lua-mode lua-indent-level) + (matlab-mode matlab-indent-level) + (mips-mode mips-tab-width) + (mustache-mode mustache-basic-offset) + (nasm-mode nasm-basic-offset) + (nginx-mode nginx-indent-level) + (nxml-mode nxml-child-indent) + (objc-mode c-basic-offset) + (octave-mode octave-block-offset) + (perl-mode perl-indent-level) + (php-mode c-basic-offset) + (pike-mode c-basic-offset) + (ps-mode ps-mode-tab) + (pug-mode pug-tab-width) + (puppet-mode puppet-indent-level) + (python-mode python-indent-offset) + (ruby-mode ruby-indent-level) + (rust-mode rust-indent-offset) + (rustic-mode rustic-indent-offset) + (scala-mode scala-indent:step) + (scss-mode css-indent-offset) + (sgml-mode sgml-basic-offset) + (sh-mode sh-basic-offset + sh-indentation) + (slim-mode slim-indent-offset) + (sml-mode sml-indent-level) + (tcl-mode tcl-indent-level + tcl-continued-indent-level) + (terra-mode terra-indent-level) + (typescript-mode typescript-indent-level) + (verilog-mode verilog-indent-level + verilog-indent-level-behavioral + verilog-indent-level-declaration + verilog-indent-level-module + verilog-cexp-indent + verilog-case-indent) + (web-mode web-mode-attr-indent-offset + web-mode-attr-value-indent-offset + web-mode-code-indent-offset + web-mode-css-indent-offset + web-mode-markup-indent-offset + web-mode-sql-indent-offset + web-mode-block-padding + web-mode-script-padding + web-mode-style-padding) + (yaml-mode yaml-indent-offset)) + "Indentation retrieving variables matched to major modes. + +Which is used when `doom-modeline-indent-info' is non-nil. +When multiple variables are specified for a mode, they will be tried resolved +in the given order." + :type '(alist :key-type symbol :value-type sexp) + :group 'doom-modeline) + +(defcustom doom-modeline-vcs-icon t + "Whether display the icon of vcs segment. + +It respects option `doom-modeline-icon'." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-vcs-max-length 15 + "The maximum displayed length of the branch name of version control." + :type 'integer + :group 'doom-modeline) + +(defcustom doom-modeline-vcs-display-function #'doom-modeline-vcs-name + "The function to display the branch name." + :type 'function + :group 'doom-modeline) + +(defcustom doom-modeline-check-icon t + "Whether display the icon of check segment. + +It respects option `doom-modeline-icon'." + :type 'boolean + :group 'doom-modeline) + +(define-obsolete-variable-alias + 'doom-modeline-checker-simple-format + 'doom-modeline-check-simple-format + "4.2.0") + +(defcustom doom-modeline-check-simple-format nil + "If non-nil, only display one number for check information if applicable." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-number-limit 99 + "The maximum number displayed for notifications." + :type 'integer + :group 'doom-modeline) + +(defcustom doom-modeline-workspace-name t + "Whether display the workspace name. + +Non-nil to display in the mode-line." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-persp-name t + "Whether display the perspective name. + +Non-nil to display in the mode-line." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-display-default-persp-name nil + "If non nil the default perspective name is displayed in the mode-line." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-persp-icon t + "If non nil the perspective name is displayed alongside a folder icon." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-repl t + "Whether display the `repl' state. + +Non-nil to display in the mode-line." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-lsp t + "Whether display the `lsp' state. + +Non-nil to display in the mode-line." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-github nil + "Whether display the GitHub notifications. + +It requires `ghub' and `async' packages. Additionally, your GitHub personal +access token must have `notifications' permissions. + +If you use `pass' to manage your secrets, you also need to add this hook: + (add-hook \\='doom-modeline-before-github-fetch-notification-hook + #\\='auth-source-pass-enable)" + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-github-interval 1800 ; (* 30 60) + "The interval of checking GitHub." + :type 'integer + :group 'doom-modeline) + +(defcustom doom-modeline-env-version t + "Whether display the environment version." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-modal t + "Whether display the modal state. + +Including `evil', `overwrite', `god', `ryo' and `xah-fly-keys', etc." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-modal-icon t + "Whether display the modal state icon. + +Including `evil', `overwrite', `god', `ryo' and `xah-fly-keys', etc." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-modal-modern-icon t + "Whether display the modern icons for modals." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-always-show-macro-register nil + "When non-nil, always show the register name when recording an evil macro." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-mu4e nil + "Whether display the mu4e notifications. + +It requires `mu4e-alert' package." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-gnus nil + "Whether to display notifications from gnus. + +It requires `gnus' to be setup" + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-gnus-timer 2 + "The wait time in minutes before gnus fetches mail. + +If nil, don't set up a hook." + :type 'integer + :group 'doom-modeline) + +(defcustom doom-modeline-gnus-idle nil + "Whether to wait an idle time to scan for news. + +When t, sets `doom-modeline-gnus-timer' as an idle timer. If a +number, Emacs must have been idle this given time, checked after +reach the defined timer, to fetch news. The time step can be +configured in `gnus-demon-timestep'." + :type '(choice + (boolean :tag "Set `doom-modeline-gnus-timer' as an idle timer") + (number :tag "Set a custom idle timer")) + :group 'doom-modeline) + +(defcustom doom-modeline-gnus-excluded-groups nil + "A list of groups to be excluded from the unread count. +Groups' names list in `gnus-newsrc-alist'`" + :type '(repeat string) + :group 'doom-modeline) + +(defcustom doom-modeline-irc t + "Whether display the irc notifications. + +It requires `circe' or `erc' package." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-irc-buffers nil + "Whether display the unread irc buffers." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-irc-stylize #'doom-modeline-shorten-irc + "Function to stylize the irc buffer names." + :type 'function + :group 'doom-modeline) + +(defcustom doom-modeline-battery t + "Whether display the battery status. + +It respects `display-battery-mode'." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-time t + "Whether display the time. + +It respects `display-time-mode'." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-display-misc-in-all-mode-lines t + "Whether display the misc segment on all mode lines. + +If nil, display only if the mode line is active." + :type 'boolean + :group 'doom-modeline) + +(defcustom doom-modeline-always-visible-segments nil + "A list of segments that should be visible even in inactive windows." + :type '(repeat symbol) + :group 'doom-modeline) + +(defcustom doom-modeline-buffer-file-name-function #'identity + "The function to handle variable `buffer-file-name'." + :type 'function + :group 'doom-modeline) + +(defcustom doom-modeline-buffer-file-truename-function #'identity + "The function to handle `buffer-file-truename'." + :type 'function + :group 'doom-modeline) + +(defcustom doom-modeline-k8s-show-namespace t + "Whether to show the current Kubernetes context's default namespace." + :type 'boolean + :group 'doom-modeline) + + +;; +;; Faces +;; + +(defgroup doom-modeline-faces nil + "The faces of `doom-modeline'." + :group 'doom-modeline + :group 'faces + :link '(url-link :tag "Homepage" "https://github.com/seagle0128/doom-modeline")) + +(defface doom-modeline + '((t ())) + "Default face." + :group 'doom-modeline-faces) + +(defface doom-modeline-emphasis + '((t (:inherit (doom-modeline mode-line-emphasis)))) + "Face used for emphasis." + :group 'doom-modeline-faces) + +(defface doom-modeline-highlight + '((t (:inherit (doom-modeline mode-line-highlight)))) + "Face used for highlighting." + :group 'doom-modeline-faces) + +(defface doom-modeline-buffer-path + '((t (:inherit (doom-modeline-emphasis bold)))) + "Face used for the dirname part of the buffer path." + :group 'doom-modeline-faces) + +(defface doom-modeline-buffer-file + '((t (:inherit (doom-modeline mode-line-buffer-id bold)))) + "Face used for the filename part of the mode-line buffer path." + :group 'doom-modeline-faces) + +(defface doom-modeline-buffer-modified + '((t (:inherit (doom-modeline warning bold) :background unspecified))) + "Face used for the \\='unsaved\\=' symbol in the mode-line." + :group 'doom-modeline-faces) + +(defface doom-modeline-buffer-major-mode + '((t (:inherit (doom-modeline-emphasis bold)))) + "Face used for the major-mode segment in the mode-line." + :group 'doom-modeline-faces) + +(defface doom-modeline-buffer-minor-mode + '((t (:inherit (doom-modeline font-lock-doc-face) :weight normal :slant normal))) + "Face used for the minor-modes segment in the mode-line." + :group 'doom-modeline-faces) + +(defface doom-modeline-project-parent-dir + '((t (:inherit (doom-modeline font-lock-comment-face bold)))) + "Face used for the project parent directory of the mode-line buffer path." + :group 'doom-modeline-faces) + +(defface doom-modeline-project-dir + '((t (:inherit (doom-modeline font-lock-string-face bold)))) + "Face used for the project directory of the mode-line buffer path." + :group 'doom-modeline-faces) + +(defface doom-modeline-project-root-dir + '((t (:inherit (doom-modeline-emphasis bold)))) + "Face used for the project part of the mode-line buffer path." + :group 'doom-modeline-faces) + +(defface doom-modeline-panel + '((t (:inherit doom-modeline-highlight))) + "Face for \\='X out of Y\\=' segments. +This applies to `anzu', `evil-substitute', `iedit' etc." + :group 'doom-modeline-faces) + +(defface doom-modeline-host + '((t (:inherit (doom-modeline italic)))) + "Face for remote hosts in the mode-line." + :group 'doom-modeline-faces) + +(defface doom-modeline-input-method + '((t (:inherit (doom-modeline-emphasis)))) + "Face for input method in the mode-line." + :group 'doom-modeline-faces) + +(defface doom-modeline-input-method-alt + '((t (:inherit (doom-modeline font-lock-doc-face) :slant normal))) + "Alternative face for input method in the mode-line." + :group 'doom-modeline-faces) + +(defface doom-modeline-debug + '((t (:inherit (doom-modeline font-lock-doc-face) :slant normal))) + "Face for debug-level messages in the mode-line. Used by vcs, check, etc." + :group 'doom-modeline-faces) + +(defface doom-modeline-info + '((t (:inherit (doom-modeline success)))) + "Face for info-level messages in the mode-line. Used by vcs, check, etc." + :group 'doom-modeline-faces) + +(defface doom-modeline-warning + '((t (:inherit (doom-modeline warning)))) + "Face for warnings in the mode-line. Used by vcs, check, etc." + :group 'doom-modeline-faces) + +(defface doom-modeline-urgent + '((t (:inherit (doom-modeline error)))) + "Face for errors in the mode-line. Used by vcs, check, etc." + :group 'doom-modeline-faces) + +(defface doom-modeline-notification + '((t (:inherit doom-modeline-warning))) + "Face for notifications in the mode-line. Used by GitHub, mu4e, etc. +Also see the face `doom-modeline-unread-number'." + :group 'doom-modeline-faces) + +(defface doom-modeline-unread-number + '((t (:inherit doom-modeline :slant italic))) + "Face for unread number in the mode-line. Used by GitHub, mu4e, etc." + :group 'doom-modeline-faces) + +(defface doom-modeline-bar + '((t (:inherit doom-modeline-highlight))) + "The face used for the left-most bar in the mode-line of an active window." + :group 'doom-modeline-faces) + +(defface doom-modeline-bar-inactive + `((t (:inherit doom-modeline))) + "The face used for the left-most bar in the mode-line of an inactive window." + :group 'doom-modeline-faces) + +(defface doom-modeline-debug-visual + '((((background light)) :foreground "#D4843E" :inherit doom-modeline) + (((background dark)) :foreground "#915B2D" :inherit doom-modeline)) + "Face to use for the mode-line while debugging." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-emacs-state + '((t (:inherit (doom-modeline font-lock-builtin-face)))) + "Face for the Emacs state tag in evil indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-insert-state + '((t (:inherit (doom-modeline font-lock-keyword-face)))) + "Face for the insert state tag in evil indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-motion-state + '((t (:inherit (doom-modeline font-lock-doc-face) :slant normal))) + "Face for the motion state tag in evil indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-normal-state + '((t (:inherit doom-modeline-info))) + "Face for the normal state tag in evil indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-operator-state + '((t (:inherit (doom-modeline mode-line)))) + "Face for the operator state tag in evil indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-visual-state + '((t (:inherit doom-modeline-warning))) + "Face for the visual state tag in evil indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-replace-state + '((t (:inherit doom-modeline-urgent))) + "Face for the replace state tag in evil indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-evil-user-state + '((t (:inherit doom-modeline-warning))) + "Face for the replace state tag in evil indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-overwrite + '((t (:inherit doom-modeline-urgent))) + "Face for overwrite indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-god + '((t (:inherit doom-modeline-info))) + "Face for god-mode indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-ryo + '((t (:inherit doom-modeline-info))) + "Face for RYO indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-fly-insert-state + '((t (:inherit (doom-modeline font-lock-keyword-face)))) + "Face for the insert state in xah-fly-keys indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-fly-normal-state + '((t (:inherit doom-modeline-info))) + "Face for the normal state in xah-fly-keys indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-boon-command-state + '((t (:inherit doom-modeline-info))) + "Face for the command state tag in boon indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-boon-insert-state + '((t (:inherit (doom-modeline font-lock-keyword-face)))) + "Face for the insert state tag in boon indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-boon-special-state + '((t (:inherit (doom-modeline font-lock-builtin-face)))) + "Face for the special state tag in boon indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-boon-off-state + '((t (:inherit (doom-modeline mode-line)))) + "Face for the off state tag in boon indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-meow-normal-state + '((t (:inherit doom-modeline-evil-normal-state))) + "Face for the normal state in meow-edit indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-meow-insert-state + '((t (:inherit doom-modeline-evil-insert-state))) + "Face for the insert state in meow-edit indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-meow-beacon-state + '((t (:inherit doom-modeline-evil-visual-state))) + "Face for the beacon state in meow-edit indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-meow-motion-state + '((t (:inherit doom-modeline-evil-motion-state))) + "Face for the motion state in meow-edit indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-meow-keypad-state + '((t (:inherit doom-modeline-evil-operator-state))) + "Face for the keypad state in meow-edit indicator." + :group 'doom-modeline-faces) + +(defface doom-modeline-persp-name + '((t (:inherit (doom-modeline font-lock-comment-face italic)))) + "Face for the persp name." + :group 'doom-modeline-faces) + +(defface doom-modeline-persp-buffer-not-in-persp + '((t (:inherit (doom-modeline font-lock-doc-face italic)))) + "Face for the buffers which are not in the persp." + :group 'doom-modeline-faces) + +(defface doom-modeline-repl-success + '((t (:inherit doom-modeline-info))) + "Face for REPL success state." + :group 'doom-modeline-faces) + +(defface doom-modeline-repl-warning + '((t (:inherit doom-modeline-warning))) + "Face for REPL warning state." + :group 'doom-modeline-faces) + +(defface doom-modeline-lsp-success + '((t (:inherit doom-modeline-info))) + "Face for LSP success state." + :group 'doom-modeline-faces) + +(defface doom-modeline-lsp-warning + '((t (:inherit doom-modeline-warning))) + "Face for LSP warning state." + :group 'doom-modeline-faces) + +(defface doom-modeline-lsp-error + '((t (:inherit doom-modeline-urgent))) + "Face for LSP error state." + :group 'doom-modeline-faces) + +(defface doom-modeline-lsp-running + '((t (:inherit (doom-modeline compilation-mode-line-run) :weight normal :slant normal))) + "Face for LSP running state." + :group 'doom-modeline-faces) + +(defface doom-modeline-battery-charging + '((t (:inherit doom-modeline-info))) + "Face for battery charging status." + :group 'doom-modeline-faces) + +(defface doom-modeline-battery-full + '((t (:inherit doom-modeline-info))) + "Face for battery full status." + :group 'doom-modeline-faces) + +(defface doom-modeline-battery-normal + '((t (:inherit (doom-modeline mode-line)))) + "Face for battery normal status." + :group 'doom-modeline-faces) + +(defface doom-modeline-battery-warning + '((t (:inherit doom-modeline-warning))) + "Face for battery warning status." + :group 'doom-modeline-faces) + +(defface doom-modeline-battery-critical + '((t (:inherit doom-modeline-urgent))) + "Face for battery critical status." + :group 'doom-modeline-faces) + +(defface doom-modeline-battery-error + '((t (:inherit doom-modeline-urgent))) + "Face for battery error status." + :group 'doom-modeline-faces) + +(defface doom-modeline-buffer-timemachine + '((t (:inherit doom-modeline-buffer-file :slant italic))) + "Face for timemachine status." + :group 'doom-modeline-faces) + +(defface doom-modeline-time + '((t (:inherit doom-modeline))) + "Face for display time." + :group 'doom-modeline-faces) + +(defface doom-modeline-compilation + '((t (:inherit doom-modeline-warning :slant italic :height 0.9))) + "Face for compilation progress." + :group 'doom-modeline-faces) + +;; +;; Externals +;; + +(defvar mode-line-right-align-edge) + +(declare-function doom-modeline-shorten-irc "doom-modeline-segments") +(declare-function face-remap-remove-relative "face-remap") +(declare-function ffip-project-root "ext:find-file-in-project") +(declare-function project-root "project") +(declare-function projectile-project-root "ext:projectile") + + +;; +;; Utilities +;; + +(defun doom-modeline-add-font-lock () + "Fontify `doom-modeline-def-*' statements." + (font-lock-add-keywords + 'emacs-lisp-mode + '(("(\\(doom-modeline-def-.+\\)\\_> +\\(.*?\\)\\_>" + (1 font-lock-keyword-face) + (2 font-lock-constant-face))))) +(doom-modeline-add-font-lock) + +(defun doom-modeline-add-imenu () + "Add to `imenu' index." + (add-to-list + 'imenu-generic-expression + '("Modelines" + "^\\s-*(\\(doom-modeline-def-modeline\\)\\s-+\\(\\(?:\\sw\\|\\s_\\|\\s'\\|\\\\.\\)+\\)" + 2)) + (add-to-list + 'imenu-generic-expression + '("Segments" + "^\\s-*(\\(doom-modeline-def-segment\\)\\s-+\\(\\(?:\\sw\\|\\s_\\|\\\\.\\)+\\)" + 2)) + (add-to-list + 'imenu-generic-expression + '("Envs" + "^\\s-*(\\(doom-modeline-def-env\\)\\s-+\\(\\(?:\\sw\\|\\s_\\|\\\\.\\)+\\)" + 2))) + + +;; +;; Core helpers +;; + +;; FIXME #183: Force to calculate mode-line height +;; @see https://github.com/seagle0128/doom-modeline/issues/183 +;; @see https://github.com/seagle0128/doom-modeline/issues/483 +(unless (>= emacs-major-version 29) + (eval-and-compile + (defun doom-modeline-redisplay (&rest _) + "Call `redisplay' to trigger mode-line height calculations. + +Certain functions, including e.g. `fit-window-to-buffer', base +their size calculations on values which are incorrect if the +mode-line has a height different from that of the `default' face +and certain other calculations have not yet taken place for the +window in question. + +These calculations can be triggered by calling `redisplay' +explicitly at the appropriate time and this functions purpose +is to make it easier to do so. + +This function is like `redisplay' with non-nil FORCE argument, +but it will only trigger a redisplay when there is a non nil +`mode-line-format' and the height of the mode-line is different +from that of the `default' face. This function is intended to be +used as an advice to window creation functions." + (when (and (bound-and-true-p doom-modeline-mode) + mode-line-format + (/= (frame-char-height) (window-mode-line-height))) + (redisplay t)))) + (advice-add #'fit-window-to-buffer :before #'doom-modeline-redisplay)) + +;; For `flycheck-color-mode-line' +(with-eval-after-load 'flycheck-color-mode-line + (defvar flycheck-color-mode-line-face-to-color) + (setq flycheck-color-mode-line-face-to-color 'doom-modeline)) + +(defun doom-modeline-icon-displayable-p () + "Return non-nil if icons are displayable." + (and doom-modeline-icon (featurep 'nerd-icons))) + +(defun doom-modeline-mwheel-available-p () + "Whether mouse wheel is available." + (and (featurep 'mwheel) (bound-and-true-p mouse-wheel-mode))) + +;; Keep `doom-modeline-current-window' up-to-date +(defun doom-modeline--selected-window () + "Get the selected window." + (frame-selected-window)) + +(defvar doom-modeline-current-window (doom-modeline--selected-window) + "Current window.") + +(defun doom-modeline--active () + "Whether is an active window." + (unless (and (bound-and-true-p mini-frame-frame) + (and (frame-live-p mini-frame-frame) + (frame-visible-p mini-frame-frame))) + (and doom-modeline-current-window + (eq (doom-modeline--selected-window) doom-modeline-current-window)))) + +(defvar-local doom-modeline--limited-width-p nil) + +(defun doom-modeline--segment-visible (name) + "Whether the segment NAME should be displayed." + (and + (or (doom-modeline--active) + (member name doom-modeline-always-visible-segments)) + (not doom-modeline--limited-width-p))) + +(defun doom-modeline-set-selected-window (&rest _) + "Set `doom-modeline-current-window' appropriately." + (let ((win (doom-modeline--selected-window))) + (setq doom-modeline-current-window + (if (minibuffer-window-active-p win) + (minibuffer-selected-window) + win)))) + +(defun doom-modeline-unset-selected-window () + "Unset `doom-modeline-current-window' appropriately." + (setq doom-modeline-current-window nil)) + +(add-hook 'pre-redisplay-functions #'doom-modeline-set-selected-window) + +;; Ensure modeline is inactive when Emacs is unfocused +(defvar doom-modeline--remap-faces '(mode-line + mode-line-active + mode-line-emphasis + mode-line-highlight + mode-line-buffer-id + doom-modeline + solaire-mode-line-face + solaire-mode-line-active-face + paradox-mode-line-face + flycheck-color-mode-line-error-face + flycheck-color-mode-line-warning-face + flycheck-color-mode-line-info-face + flycheck-color-mode-line-success-face)) + +(defvar doom-modeline--remap-face-cookie-alist nil) +(defun doom-modeline-focus () + "Focus mode-line." + (mapc #'face-remap-remove-relative doom-modeline--remap-face-cookie-alist)) + +(defun doom-modeline-unfocus () + "Unfocus mode-line." + (dolist (face doom-modeline--remap-faces) + (add-to-list 'doom-modeline--remap-face-cookie-alist + (face-remap-add-relative face 'mode-line-inactive)))) + +(with-no-warnings + (if (boundp 'after-focus-change-function) + (progn + (defun doom-modeline-focus-change (&rest _) + (if (frame-focus-state (frame-parent)) + (progn + (doom-modeline-focus) + ;; HACK: pulse after focusing in the frame to refresh the buffer name. + ;; @see https://github.com/seagle0128/doom-modeline/issues/591 + (when (fboundp 'pulse-momentary-highlight-region) + (pulse-momentary-highlight-region 0 0))) + (doom-modeline-unfocus))) + (advice-add #'handle-switch-frame :after #'doom-modeline-focus-change) + (add-function :after after-focus-change-function #'doom-modeline-focus-change)) + (progn + (add-hook 'focus-in-hook #'doom-modeline-focus) + (add-hook 'focus-out-hook #'doom-modeline-unfocus)))) + + +;; +;; Core +;; + +(defvar doom-modeline-fn-alist ()) +(defvar doom-modeline-var-alist ()) + +(defmacro doom-modeline-def-segment (name &rest body) + "Define a modeline segment NAME with BODY and byte compiles it." + (declare (indent defun) (doc-string 2)) + (let ((sym (intern (format "doom-modeline-segment--%s" name))) + (docstring (if (stringp (car body)) + (pop body) + (format "%s modeline segment" name)))) + (cond ((and (symbolp (car body)) + (not (cdr body))) + `(add-to-list 'doom-modeline-var-alist (cons ',name ',(car body)))) + (t + `(progn + (defun ,sym () ,docstring ,@body) + (add-to-list 'doom-modeline-fn-alist (cons ',name ',sym)) + ,(unless (bound-and-true-p byte-compile-current-file) + `(let (byte-compile-warnings) + (unless (and (fboundp 'subr-native-elisp-p) + (subr-native-elisp-p (symbol-function #',sym))) + (byte-compile #',sym))))))))) + +(defun doom-modeline--prepare-segments (segments) + "Prepare mode-line `SEGMENTS'." + (let (forms it) + (dolist (seg segments) + (cond ((stringp seg) + (push seg forms)) + ((symbolp seg) + (cond ((setq it (alist-get seg doom-modeline-fn-alist)) + (push (list :eval (list it)) forms)) + ((setq it (alist-get seg doom-modeline-var-alist)) + (push it forms)) + ((error "%s is not a defined segment" seg)))) + ((error "%s is not a valid segment" seg)))) + (nreverse forms))) + +(defun doom-modeline-def-modeline (name lhs &optional rhs) + "Define a modeline format and byte-compiles it. +NAME is a symbol to identify it (used by `doom-modeline' for retrieval). +LHS and RHS are lists of symbols of modeline segments defined with +`doom-modeline-def-segment'. + +Example: + (doom-modeline-def-modeline \\='minimal + \\='(bar matches \" \" buffer-info) + \\='(media-info major-mode)) + (doom-modeline-set-modeline \\='minimal t)" + (let ((sym (intern (format "doom-modeline-format--%s" name))) + (lhs-forms (doom-modeline--prepare-segments lhs)) + (rhs-forms (doom-modeline--prepare-segments rhs))) + (defalias sym + (lambda () + (list lhs-forms + (let* ((rhs-str (format-mode-line (cons "" rhs-forms))) + (rhs-width (progn + (add-face-text-property + 0 (length rhs-str) 'mode-line t rhs-str) + (doom-modeline-string-pixel-width rhs-str)))) + (propertize + " " + 'face (doom-modeline-face) + 'display + ;; Backport from `mode-line-right-align-edge' in 30 + (if (and (display-graphic-p) + (not (eq mode-line-right-align-edge 'window))) + `(space :align-to (- ,mode-line-right-align-edge + (,rhs-width))) + `(space :align-to (,(- (window-pixel-width) + (window-scroll-bar-width) + (window-right-divider-width) + (* (or (cdr (window-margins)) 1) + (frame-char-width)) + (pcase mode-line-right-align-edge + ('right-margin + (or (cdr (window-margins)) 0)) + ('right-fringe + (or (cadr (window-fringes)) 0)) + (_ 0)) + rhs-width)))))) + rhs-forms)) + (concat "Modeline:\n" + (format " %s\n %s" + (prin1-to-string lhs) + (prin1-to-string rhs)))))) +(put 'doom-modeline-def-modeline 'lisp-indent-function 'defun) + +(defun doom-modeline (key) + "Return a mode-line configuration associated with KEY (a symbol). +Throws an error if it doesn't exist." + (let ((fn (intern-soft (format "doom-modeline-format--%s" key)))) + (when (functionp fn) + `(:eval (,fn))))) + +(defun doom-modeline-set-modeline (key &optional default) + "Set the modeline format. Does nothing if the modeline KEY doesn't exist. +If DEFAULT is non-nil, set the default mode-line for all buffers." + (when-let ((modeline (doom-modeline key))) + (setf (if default + (default-value 'mode-line-format) + mode-line-format) + (list "%e" modeline)))) + +;; +;; Helpers +;; + +(defconst doom-modeline-ellipsis + (if (char-displayable-p ?…) "…" "...") + "Ellipsis.") + +(defsubst doom-modeline-spc () + "Whitespace." + (propertize " " 'face (doom-modeline-face))) + +(defsubst doom-modeline-wspc () + "Wide Whitespace." + (propertize " " 'face (doom-modeline-face))) + +(defsubst doom-modeline-vspc () + "Thin whitespace." + (propertize " " + 'face (doom-modeline-face) + 'display '((space :relative-width 0.5)))) + +(defun doom-modeline-face (&optional face inactive-face) + "Display FACE in active window, and INACTIVE-FACE in inactive window. +IF FACE is nil, `mode-line' face will be used. +If INACTIVE-FACE is nil, `mode-line-inactive' face will be used." + (if (doom-modeline--active) + (or (and (facep face) `(:inherit (doom-modeline ,face))) + (and (facep 'mode-line-active) '(:inherit (doom-modeline mode-line-active))) + '(:inherit (doom-modeline mode-line))) + (or (and (facep face) `(:inherit (doom-modeline mode-line-inactive ,face))) + (and (facep inactive-face) `(:inherit (doom-modeline ,inactive-face))) + '(:inherit (doom-modeline mode-line-inactive))))) + +(defun doom-modeline-string-pixel-width (str) + "Return the width of STR in pixels." + (if (fboundp 'string-pixel-width) + (string-pixel-width str) + (* (string-width str) (window-font-width nil 'mode-line) + (if (display-graphic-p) 1.05 1.0)))) + +(defun doom-modeline--font-height () + "Calculate the actual char height of the mode-line." + (let ((height (face-attribute 'mode-line :height)) + (char-height (window-font-height nil 'mode-line))) + (round + (* 1.0 (cond ((integerp height) (/ height 10)) + ((floatp height) (* height char-height)) + (t char-height)))))) + +(defun doom-modeline--original-value (sym) + "Return the original value for SYM, if any. + +If SYM has an original value, return it in a list. Return nil +otherwise." + (let* ((orig-val-expr (get sym 'standard-value))) + (when (consp orig-val-expr) + (ignore-errors + (list + (eval (car orig-val-expr))))))) + +(defun doom-modeline-add-variable-watcher (symbol watch-function) + "Cause WATCH-FUNCTION to be called when SYMBOL is set if possible. + +See docs of `add-variable-watcher'." + (when (fboundp 'add-variable-watcher) + (add-variable-watcher symbol watch-function))) + +(defun doom-modeline-propertize-icon (icon &optional face) + "Propertize the ICON with the specified FACE. + +The face should be the first attribute, or the font family may be overridden. +So convert the face \":family XXX :height XXX :inherit XXX\" to +\":inherit XXX :family XXX :height XXX\". +See https://github.com/seagle0128/doom-modeline/issues/301." + (when icon + (if (doom-modeline-icon-displayable-p) + (when-let ((props (get-text-property 0 'face icon))) + (when (listp props) + (cl-destructuring-bind (&key family height inherit &allow-other-keys) props + (propertize icon 'face `(:inherit (doom-modeline ,(or face inherit props)) + :family ,(or family "") + :height ,(or height 1.0)))))) + (propertize icon 'face `(:inherit (doom-modeline ,face)))))) + +(defun doom-modeline-icon (icon-set icon-name unicode text &rest args) + "Display icon of ICON-NAME with ARGS in mode-line. + +ICON-SET includes `ipsicon', `octicon', `pomicon', `powerline', `faicon', +`wicon', `sucicon', `devicon', `codicon', `flicon' and `mdicon', etc. +UNICODE is the unicode char fallback. TEXT is the ASCII char fallback. +ARGS is same as `nerd-icons-octicon' and others." + (let ((face `(:inherit (doom-modeline + ,(or (plist-get args :face) 'mode-line))))) + (cond + ;; Icon + ((and (doom-modeline-icon-displayable-p) + icon-name + (not (string-empty-p icon-name))) + (if-let* ((func (nerd-icons--function-name icon-set)) + (icon (and (fboundp func) + (apply func icon-name args)))) + (doom-modeline-propertize-icon icon face) + "")) + ;; Unicode fallback + ((and doom-modeline-unicode-fallback + unicode + (not (string-empty-p unicode)) + (char-displayable-p (string-to-char unicode))) + (propertize unicode 'face face)) + ;; ASCII text + (text + (propertize text 'face face)) + ;; Fallback + (t "")))) + +(defun doom-modeline-icon-for-buffer () + "Get the formatted icon for the current buffer." + (nerd-icons-icon-for-buffer)) + +(defun doom-modeline-display-icon (icon) + "Display ICON in mode-line." + (if (doom-modeline--active) + icon + (doom-modeline-propertize-icon icon 'mode-line-inactive))) + +(defun doom-modeline-display-text (text) + "Display TEXT in mode-line." + (if (doom-modeline--active) + text + (propertize text 'face `(:inherit (mode-line-inactive + ,(get-text-property 0 'face text)))))) + +(defun doom-modeline-vcs-name () + "Display the vcs name." + (and vc-mode (cadr (split-string (string-trim vc-mode) "^[A-Z]+[-:]+")))) + +(defun doom-modeline--create-bar-image (face width height) + "Create the bar image. + +Use FACE for the bar, WIDTH and HEIGHT are the image size in pixels." + (when (and (image-type-available-p 'pbm) + (numberp width) (> width 0) + (numberp height) (> height 0)) + (propertize + " " 'display + (let ((color (or (face-background face nil t) "None"))) + (ignore-errors + (create-image + (concat (format "P1\n%i %i\n" width height) + (make-string (* width height) ?1) + "\n") + 'pbm t :scale 1 :foreground color :ascent 'center)))))) + +(defun doom-modeline--create-hud-image + (face1 face2 width height top-margin bottom-margin) + "Create the hud image. + +Use FACE1 for the bar, FACE2 for the background. +WIDTH and HEIGHT are the image size in pixels. +TOP-MARGIN and BOTTOM-MARGIN are the size of the margin above and below the bar, +respectively." + (when (and (display-graphic-p) + (image-type-available-p 'pbm) + (numberp width) (> width 0) + (numberp height) (> height 0)) + (let ((min-height (min height doom-modeline-hud-min-height))) + (unless (> (- height top-margin bottom-margin) min-height) + (let ((margin (- height min-height))) + (setq top-margin (/ (* margin top-margin) (+ top-margin bottom-margin)) + bottom-margin (- margin top-margin))))) + (propertize + " " 'display + (let ((color1 (or (face-background face1 nil t) "None")) + (color2 (or (face-background face2 nil t) "None"))) + (create-image + (concat + (format "P1\n%i %i\n" width height) + (make-string (* top-margin width) ?0) + (make-string (* (- height top-margin bottom-margin) width) ?1) + (make-string (* bottom-margin width) ?0) + "\n") + 'pbm t :foreground color1 :background color2 :ascent 'center))))) + +;; Check whether `window-total-width' is smaller than the limit +(defun doom-modeline-window-size-change-function (&rest _) + "Function for `window-size-change-functions'." + (setq doom-modeline--limited-width-p + (cond + ((integerp doom-modeline-window-width-limit) + (<= (window-total-width) doom-modeline-window-width-limit)) + ((floatp doom-modeline-window-width-limit) + (<= (/ (window-total-width) (frame-width) 1.0) + doom-modeline-window-width-limit))))) + +(add-hook 'after-revert-hook #'doom-modeline-window-size-change-function) +(add-hook 'buffer-list-update-hook #'doom-modeline-window-size-change-function) +(add-hook 'window-size-change-functions #'doom-modeline-window-size-change-function) + +(defvar-local doom-modeline--project-root nil) +(defun doom-modeline--project-root () + "Get the path to the project root. +Return nil if no project was found." + (or doom-modeline--project-root + (setq doom-modeline--project-root + (cond + ((and (memq doom-modeline-project-detection '(auto ffip)) + (fboundp 'ffip-project-root)) + (let ((inhibit-message t)) + (ffip-project-root))) + ((and (memq doom-modeline-project-detection '(auto projectile)) + (bound-and-true-p projectile-mode)) + (projectile-project-root)) + ((and (memq doom-modeline-project-detection '(auto project)) + (fboundp 'project-current)) + (when-let ((project (project-current))) + (expand-file-name + (if (fboundp 'project-root) + (project-root project) + (car (with-no-warnings + (project-roots project))))))))))) + +(doom-modeline-add-variable-watcher + 'doom-modeline-project-detection + (lambda (_sym val op _where) + (when (eq op 'set) + (setq doom-modeline-project-detection val) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (setq doom-modeline--project-root nil) + (and buffer-file-name (revert-buffer t t))))))) + +(defun doom-modeline-project-p () + "Check if the file is in a project." + (doom-modeline--project-root)) + +(defun doom-modeline-project-root () + "Get the path to the root of your project. +Return `default-directory' if no project was found." + (abbreviate-file-name + (or (doom-modeline--project-root) default-directory))) + +(defun doom-modeline--format-buffer-file-name () + "Get and format the buffer file name." + (let ((buffer-file-name (file-local-name + (or (buffer-file-name (buffer-base-buffer)) "")))) + (or (and doom-modeline-buffer-file-name-function + (funcall doom-modeline-buffer-file-name-function buffer-file-name)) + buffer-file-name))) + +(defun doom-modeline--format-buffer-file-truename (b-f-n) + "Get and format buffer file truename via B-F-N." + (let ((buffer-file-truename (file-local-name + (or (file-truename b-f-n) "")))) + (or (and doom-modeline-buffer-file-truename-function + (funcall doom-modeline-buffer-file-truename-function buffer-file-truename)) + buffer-file-truename))) + +(defun doom-modeline-buffer-file-name () + "Propertize file name based on `doom-modeline-buffer-file-name-style'." + (let* ((buffer-file-name (doom-modeline--format-buffer-file-name)) + (buffer-file-truename (doom-modeline--format-buffer-file-truename buffer-file-name)) + (file-name + (pcase doom-modeline-buffer-file-name-style + ('auto + (if (doom-modeline-project-p) + (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink 'shrink 'hide) + (propertize "%b" 'face 'doom-modeline-buffer-file))) + ('truncate-upto-project + (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink)) + ('truncate-from-project + (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename nil 'shrink)) + ('truncate-with-project + (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink 'shrink 'hide)) + ('truncate-except-project + (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink 'shrink)) + ('truncate-upto-root + (doom-modeline--buffer-file-name-truncate buffer-file-name buffer-file-truename)) + ('truncate-all + (doom-modeline--buffer-file-name-truncate buffer-file-name buffer-file-truename t)) + ('truncate-nil + (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename)) + ('relative-to-project + (doom-modeline--buffer-file-name-relative buffer-file-name buffer-file-truename)) + ('relative-from-project + (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename nil nil 'hide)) + ('file-name + (propertize (file-name-nondirectory buffer-file-name) + 'face 'doom-modeline-buffer-file)) + ('file-name-with-project + (format "%s|%s" + (propertize (file-name-nondirectory + (directory-file-name (file-local-name (doom-modeline-project-root)))) + 'face 'doom-modeline-project-dir) + (propertize (file-name-nondirectory buffer-file-name) + 'face 'doom-modeline-buffer-file))) + ((or 'buffer-name _) + (propertize "%b" 'face 'doom-modeline-buffer-file))))) + (propertize (if (string-empty-p file-name) + (propertize "%b" 'face 'doom-modeline-buffer-file) + file-name) + 'mouse-face 'mode-line-highlight + 'help-echo (concat buffer-file-truename + (unless (string= (file-name-nondirectory buffer-file-truename) + (buffer-name)) + (concat "\n" (buffer-name))) + "\nmouse-1: Previous buffer\nmouse-3: Next buffer") + 'local-map mode-line-buffer-identification-keymap))) + +(defun doom-modeline--buffer-file-name-truncate (file-path true-file-path &optional truncate-tail) + "Propertize file name that truncates every dir along path. + +If TRUNCATE-TAIL is t also truncate the parent directory of the file." + (let ((dirs (shrink-path-prompt (file-name-directory true-file-path)))) + (if (null dirs) + (propertize "%b" 'face 'doom-modeline-buffer-file) + (let ((dirname (car dirs)) + (basename (cdr dirs))) + (concat (propertize (concat dirname + (if truncate-tail (substring basename 0 1) basename) + "/") + 'face 'doom-modeline-project-root-dir) + (propertize (file-name-nondirectory file-path) + 'face 'doom-modeline-buffer-file)))))) + +(defun doom-modeline--buffer-file-name-relative (_file-path true-file-path &optional include-project) + "Propertize file name showing directories relative to project's root only. + +If INCLUDE-PROJECT is non-nil, the project path will be included." + (let ((root (file-local-name (doom-modeline-project-root)))) + (if (null root) + (propertize "%b" 'face 'doom-modeline-buffer-file) + (let ((relative-dirs (file-relative-name (file-name-directory true-file-path) + (if include-project (concat root "../") root)))) + (and (equal "./" relative-dirs) (setq relative-dirs "")) + (concat (propertize relative-dirs 'face 'doom-modeline-buffer-path) + (propertize (file-name-nondirectory true-file-path) + 'face 'doom-modeline-buffer-file)))))) + +(defun doom-modeline--buffer-file-name (file-path + true-file-path + &optional + truncate-project-root-parent + truncate-project-relative-path + hide-project-root-parent) + "Propertize buffer name given by FILE-PATH or TRUE-FILE-PATH. + +If TRUNCATE-PROJECT-ROOT-PARENT is non-nil will be saved by truncating project +root parent down fish-shell style. + +Example: + ~/Projects/FOSS/emacs/lisp/comint.el => ~/P/F/emacs/lisp/comint.el + +If TRUNCATE-PROJECT-RELATIVE-PATH is non-nil will be saved by truncating project +relative path down fish-shell style. + +Example: + ~/Projects/FOSS/emacs/lisp/comint.el => ~/Projects/FOSS/emacs/l/comint.el + +If HIDE-PROJECT-ROOT-PARENT is non-nil will hide project root parent. + +Example: + ~/Projects/FOSS/emacs/lisp/comint.el => emacs/lisp/comint.el" + (let ((project-root (file-local-name (doom-modeline-project-root)))) + (concat + ;; Project root parent + (unless hide-project-root-parent + (when-let (root-path-parent + (file-name-directory (directory-file-name project-root))) + (propertize + (if (and truncate-project-root-parent + (not (string-empty-p root-path-parent)) + (not (string= root-path-parent "/"))) + (shrink-path--dirs-internal root-path-parent t) + (abbreviate-file-name root-path-parent)) + 'face 'doom-modeline-project-parent-dir))) + ;; Project directory + (propertize + (concat (file-name-nondirectory (directory-file-name project-root)) "/") + 'face 'doom-modeline-project-dir) + ;; relative path + (propertize + (when-let (relative-path (file-relative-name + (or (file-name-directory + (if doom-modeline-buffer-file-true-name + true-file-path file-path)) + "./") + project-root)) + (if (string= relative-path "./") + "" + (if truncate-project-relative-path + (substring (shrink-path--dirs-internal relative-path t) 1) + relative-path))) + 'face 'doom-modeline-buffer-path) + ;; File name + (propertize (file-name-nondirectory file-path) + 'face 'doom-modeline-buffer-file)))) + +(provide 'doom-modeline-core) + +;;; doom-modeline-core.el ends here diff --git a/emacs/elpa/doom-modeline-20240810.1702/doom-modeline-core.elc b/emacs/elpa/doom-modeline-20240811.1437/doom-modeline-core.elc Binary files differ. diff --git a/emacs/elpa/doom-modeline-20240810.1702/doom-modeline-env.el b/emacs/elpa/doom-modeline-20240811.1437/doom-modeline-env.el diff --git a/emacs/elpa/doom-modeline-20240810.1702/doom-modeline-env.elc b/emacs/elpa/doom-modeline-20240811.1437/doom-modeline-env.elc Binary files differ. diff --git a/emacs/elpa/doom-modeline-20240811.1437/doom-modeline-pkg.el b/emacs/elpa/doom-modeline-20240811.1437/doom-modeline-pkg.el @@ -0,0 +1,17 @@ +(define-package "doom-modeline" "20240811.1437" "A minimal and modern mode-line" + '((emacs "25.1") + (compat "29.1.4.5") + (nerd-icons "0.1.0") + (shrink-path "0.3.1")) + :commit "790e6817814a1fa893a91722861cba9424b0f004" :authors + '(("Vincent Zhang" . "seagle0128@gmail.com")) + :maintainers + '(("Vincent Zhang" . "seagle0128@gmail.com")) + :maintainer + '("Vincent Zhang" . "seagle0128@gmail.com") + :keywords + '("faces" "mode-line") + :url "https://github.com/seagle0128/doom-modeline") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs/elpa/doom-modeline-20240810.1702/doom-modeline-segments.el b/emacs/elpa/doom-modeline-20240811.1437/doom-modeline-segments.el diff --git a/emacs/elpa/doom-modeline-20240811.1437/doom-modeline-segments.elc b/emacs/elpa/doom-modeline-20240811.1437/doom-modeline-segments.elc Binary files differ. diff --git a/emacs/elpa/doom-modeline-20240810.1702/doom-modeline.el b/emacs/elpa/doom-modeline-20240811.1437/doom-modeline.el diff --git a/emacs/elpa/doom-modeline-20240810.1702/doom-modeline.elc b/emacs/elpa/doom-modeline-20240811.1437/doom-modeline.elc Binary files differ. diff --git a/emacs/elpa/git-commit-20240808.1852/git-commit-pkg.el b/emacs/elpa/git-commit-20240808.1852/git-commit-pkg.el @@ -1,18 +0,0 @@ -(define-package "git-commit" "20240808.1852" "Edit Git commit messages." - '((emacs "26.1") - (compat "30.0.0.0") - (transient "0.7.4") - (with-editor "3.4.1")) - :commit "020aca7c9c4154dbc4a72acbd56165ecccea1bf1" :authors - '(("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") - ("Sebastian Wiesner" . "lunaryorn@gmail.com") - ("Florian Ragwitz" . "rafl@debian.org") - ("Marius Vollmer" . "marius.vollmer@gmail.com")) - :maintainer - '("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") - :keywords - '("git" "tools" "vc") - :url "https://github.com/magit/magit") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/git-commit-20240808.1852/git-commit.elc b/emacs/elpa/git-commit-20240808.1852/git-commit.elc Binary files differ. diff --git a/emacs/elpa/git-commit-20240808.1852/git-commit-autoloads.el b/emacs/elpa/git-commit-20240811.1419/git-commit-autoloads.el diff --git a/emacs/elpa/git-commit-20240811.1419/git-commit-pkg.el b/emacs/elpa/git-commit-20240811.1419/git-commit-pkg.el @@ -0,0 +1,18 @@ +(define-package "git-commit" "20240811.1419" "Edit Git commit messages." + '((emacs "26.1") + (compat "30.0.0.0") + (transient "20240805") + (with-editor "20240806")) + :commit "a2739d7db1fdf19b95f36f6ddd15b0c1f523bd26" :authors + '(("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") + ("Sebastian Wiesner" . "lunaryorn@gmail.com") + ("Florian Ragwitz" . "rafl@debian.org") + ("Marius Vollmer" . "marius.vollmer@gmail.com")) + :maintainer + '("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") + :keywords + '("git" "tools" "vc") + :url "https://github.com/magit/magit") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs/elpa/git-commit-20240808.1852/git-commit.el b/emacs/elpa/git-commit-20240811.1419/git-commit.el diff --git a/emacs/elpa/git-commit-20240811.1419/git-commit.elc b/emacs/elpa/git-commit-20240811.1419/git-commit.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-diff.elc b/emacs/elpa/magit-20240808.1852/magit-diff.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-log.elc b/emacs/elpa/magit-20240808.1852/magit-log.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-pkg.el b/emacs/elpa/magit-20240808.1852/magit-pkg.el @@ -1,20 +0,0 @@ -(define-package "magit" "20240808.1852" "A Git porcelain inside Emacs." - '((emacs "26.1") - (compat "30.0.0.0") - (dash "2.19.1") - (git-commit "4.0.0") - (magit-section "4.0.0") - (seq "2.24") - (transient "0.7.4") - (with-editor "3.4.1")) - :commit "020aca7c9c4154dbc4a72acbd56165ecccea1bf1" :authors - '(("Marius Vollmer" . "marius.vollmer@gmail.com") - ("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev")) - :maintainer - '("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") - :keywords - '("git" "tools" "vc") - :url "https://github.com/magit/magit") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/magit-20240808.1852/magit-stash.elc b/emacs/elpa/magit-20240808.1852/magit-stash.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/AUTHORS.md b/emacs/elpa/magit-20240811.1419/AUTHORS.md diff --git a/emacs/elpa/magit-20240808.1852/LICENSE b/emacs/elpa/magit-20240811.1419/LICENSE diff --git a/emacs/elpa/magit-20240808.1852/dir b/emacs/elpa/magit-20240811.1419/dir diff --git a/emacs/elpa/magit-20240808.1852/git-rebase.el b/emacs/elpa/magit-20240811.1419/git-rebase.el diff --git a/emacs/elpa/magit-20240808.1852/git-rebase.elc b/emacs/elpa/magit-20240811.1419/git-rebase.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-apply.el b/emacs/elpa/magit-20240811.1419/magit-apply.el diff --git a/emacs/elpa/magit-20240808.1852/magit-apply.elc b/emacs/elpa/magit-20240811.1419/magit-apply.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-autoloads.el b/emacs/elpa/magit-20240811.1419/magit-autoloads.el diff --git a/emacs/elpa/magit-20240808.1852/magit-autorevert.el b/emacs/elpa/magit-20240811.1419/magit-autorevert.el diff --git a/emacs/elpa/magit-20240808.1852/magit-autorevert.elc b/emacs/elpa/magit-20240811.1419/magit-autorevert.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-base.el b/emacs/elpa/magit-20240811.1419/magit-base.el diff --git a/emacs/elpa/magit-20240808.1852/magit-base.elc b/emacs/elpa/magit-20240811.1419/magit-base.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-bisect.el b/emacs/elpa/magit-20240811.1419/magit-bisect.el diff --git a/emacs/elpa/magit-20240808.1852/magit-bisect.elc b/emacs/elpa/magit-20240811.1419/magit-bisect.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-blame.el b/emacs/elpa/magit-20240811.1419/magit-blame.el diff --git a/emacs/elpa/magit-20240808.1852/magit-blame.elc b/emacs/elpa/magit-20240811.1419/magit-blame.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-bookmark.el b/emacs/elpa/magit-20240811.1419/magit-bookmark.el diff --git a/emacs/elpa/magit-20240808.1852/magit-bookmark.elc b/emacs/elpa/magit-20240811.1419/magit-bookmark.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-branch.el b/emacs/elpa/magit-20240811.1419/magit-branch.el diff --git a/emacs/elpa/magit-20240808.1852/magit-branch.elc b/emacs/elpa/magit-20240811.1419/magit-branch.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-bundle.el b/emacs/elpa/magit-20240811.1419/magit-bundle.el diff --git a/emacs/elpa/magit-20240808.1852/magit-bundle.elc b/emacs/elpa/magit-20240811.1419/magit-bundle.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-clone.el b/emacs/elpa/magit-20240811.1419/magit-clone.el diff --git a/emacs/elpa/magit-20240808.1852/magit-clone.elc b/emacs/elpa/magit-20240811.1419/magit-clone.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-commit.el b/emacs/elpa/magit-20240811.1419/magit-commit.el diff --git a/emacs/elpa/magit-20240808.1852/magit-commit.elc b/emacs/elpa/magit-20240811.1419/magit-commit.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-core.el b/emacs/elpa/magit-20240811.1419/magit-core.el diff --git a/emacs/elpa/magit-20240808.1852/magit-core.elc b/emacs/elpa/magit-20240811.1419/magit-core.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-diff.el b/emacs/elpa/magit-20240811.1419/magit-diff.el diff --git a/emacs/elpa/magit-20240811.1419/magit-diff.elc b/emacs/elpa/magit-20240811.1419/magit-diff.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-ediff.el b/emacs/elpa/magit-20240811.1419/magit-ediff.el diff --git a/emacs/elpa/magit-20240808.1852/magit-ediff.elc b/emacs/elpa/magit-20240811.1419/magit-ediff.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-extras.el b/emacs/elpa/magit-20240811.1419/magit-extras.el diff --git a/emacs/elpa/magit-20240808.1852/magit-extras.elc b/emacs/elpa/magit-20240811.1419/magit-extras.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-fetch.el b/emacs/elpa/magit-20240811.1419/magit-fetch.el diff --git a/emacs/elpa/magit-20240808.1852/magit-fetch.elc b/emacs/elpa/magit-20240811.1419/magit-fetch.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-files.el b/emacs/elpa/magit-20240811.1419/magit-files.el diff --git a/emacs/elpa/magit-20240808.1852/magit-files.elc b/emacs/elpa/magit-20240811.1419/magit-files.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-git.el b/emacs/elpa/magit-20240811.1419/magit-git.el diff --git a/emacs/elpa/magit-20240808.1852/magit-git.elc b/emacs/elpa/magit-20240811.1419/magit-git.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-gitignore.el b/emacs/elpa/magit-20240811.1419/magit-gitignore.el diff --git a/emacs/elpa/magit-20240808.1852/magit-gitignore.elc b/emacs/elpa/magit-20240811.1419/magit-gitignore.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-log.el b/emacs/elpa/magit-20240811.1419/magit-log.el diff --git a/emacs/elpa/magit-20240811.1419/magit-log.elc b/emacs/elpa/magit-20240811.1419/magit-log.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-margin.el b/emacs/elpa/magit-20240811.1419/magit-margin.el diff --git a/emacs/elpa/magit-20240808.1852/magit-margin.elc b/emacs/elpa/magit-20240811.1419/magit-margin.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-merge.el b/emacs/elpa/magit-20240811.1419/magit-merge.el diff --git a/emacs/elpa/magit-20240808.1852/magit-merge.elc b/emacs/elpa/magit-20240811.1419/magit-merge.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-mode.el b/emacs/elpa/magit-20240811.1419/magit-mode.el diff --git a/emacs/elpa/magit-20240808.1852/magit-mode.elc b/emacs/elpa/magit-20240811.1419/magit-mode.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-notes.el b/emacs/elpa/magit-20240811.1419/magit-notes.el diff --git a/emacs/elpa/magit-20240808.1852/magit-notes.elc b/emacs/elpa/magit-20240811.1419/magit-notes.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-patch.el b/emacs/elpa/magit-20240811.1419/magit-patch.el diff --git a/emacs/elpa/magit-20240808.1852/magit-patch.elc b/emacs/elpa/magit-20240811.1419/magit-patch.elc Binary files differ. diff --git a/emacs/elpa/magit-20240811.1419/magit-pkg.el b/emacs/elpa/magit-20240811.1419/magit-pkg.el @@ -0,0 +1,20 @@ +(define-package "magit" "20240811.1419" "A Git porcelain inside Emacs." + '((emacs "26.1") + (compat "30.0.0.0") + (dash "20240510") + (git-commit "20240808") + (magit-section "20240808") + (seq "2.24") + (transient "20240805") + (with-editor "20240806")) + :commit "a2739d7db1fdf19b95f36f6ddd15b0c1f523bd26" :authors + '(("Marius Vollmer" . "marius.vollmer@gmail.com") + ("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev")) + :maintainer + '("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") + :keywords + '("git" "tools" "vc") + :url "https://github.com/magit/magit") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs/elpa/magit-20240808.1852/magit-process.el b/emacs/elpa/magit-20240811.1419/magit-process.el diff --git a/emacs/elpa/magit-20240808.1852/magit-process.elc b/emacs/elpa/magit-20240811.1419/magit-process.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-pull.el b/emacs/elpa/magit-20240811.1419/magit-pull.el diff --git a/emacs/elpa/magit-20240808.1852/magit-pull.elc b/emacs/elpa/magit-20240811.1419/magit-pull.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-push.el b/emacs/elpa/magit-20240811.1419/magit-push.el diff --git a/emacs/elpa/magit-20240808.1852/magit-push.elc b/emacs/elpa/magit-20240811.1419/magit-push.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-reflog.el b/emacs/elpa/magit-20240811.1419/magit-reflog.el diff --git a/emacs/elpa/magit-20240808.1852/magit-reflog.elc b/emacs/elpa/magit-20240811.1419/magit-reflog.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-refs.el b/emacs/elpa/magit-20240811.1419/magit-refs.el diff --git a/emacs/elpa/magit-20240808.1852/magit-refs.elc b/emacs/elpa/magit-20240811.1419/magit-refs.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-remote.el b/emacs/elpa/magit-20240811.1419/magit-remote.el diff --git a/emacs/elpa/magit-20240808.1852/magit-remote.elc b/emacs/elpa/magit-20240811.1419/magit-remote.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-repos.el b/emacs/elpa/magit-20240811.1419/magit-repos.el diff --git a/emacs/elpa/magit-20240808.1852/magit-repos.elc b/emacs/elpa/magit-20240811.1419/magit-repos.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-reset.el b/emacs/elpa/magit-20240811.1419/magit-reset.el diff --git a/emacs/elpa/magit-20240808.1852/magit-reset.elc b/emacs/elpa/magit-20240811.1419/magit-reset.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-sequence.el b/emacs/elpa/magit-20240811.1419/magit-sequence.el diff --git a/emacs/elpa/magit-20240808.1852/magit-sequence.elc b/emacs/elpa/magit-20240811.1419/magit-sequence.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-sparse-checkout.el b/emacs/elpa/magit-20240811.1419/magit-sparse-checkout.el diff --git a/emacs/elpa/magit-20240808.1852/magit-sparse-checkout.elc b/emacs/elpa/magit-20240811.1419/magit-sparse-checkout.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-stash.el b/emacs/elpa/magit-20240811.1419/magit-stash.el diff --git a/emacs/elpa/magit-20240811.1419/magit-stash.elc b/emacs/elpa/magit-20240811.1419/magit-stash.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-status.el b/emacs/elpa/magit-20240811.1419/magit-status.el diff --git a/emacs/elpa/magit-20240808.1852/magit-status.elc b/emacs/elpa/magit-20240811.1419/magit-status.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-submodule.el b/emacs/elpa/magit-20240811.1419/magit-submodule.el diff --git a/emacs/elpa/magit-20240808.1852/magit-submodule.elc b/emacs/elpa/magit-20240811.1419/magit-submodule.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-subtree.el b/emacs/elpa/magit-20240811.1419/magit-subtree.el diff --git a/emacs/elpa/magit-20240808.1852/magit-subtree.elc b/emacs/elpa/magit-20240811.1419/magit-subtree.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-tag.el b/emacs/elpa/magit-20240811.1419/magit-tag.el diff --git a/emacs/elpa/magit-20240808.1852/magit-tag.elc b/emacs/elpa/magit-20240811.1419/magit-tag.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-transient.el b/emacs/elpa/magit-20240811.1419/magit-transient.el diff --git a/emacs/elpa/magit-20240808.1852/magit-transient.elc b/emacs/elpa/magit-20240811.1419/magit-transient.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-wip.el b/emacs/elpa/magit-20240811.1419/magit-wip.el diff --git a/emacs/elpa/magit-20240808.1852/magit-wip.elc b/emacs/elpa/magit-20240811.1419/magit-wip.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit-worktree.el b/emacs/elpa/magit-20240811.1419/magit-worktree.el diff --git a/emacs/elpa/magit-20240808.1852/magit-worktree.elc b/emacs/elpa/magit-20240811.1419/magit-worktree.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit.el b/emacs/elpa/magit-20240811.1419/magit.el diff --git a/emacs/elpa/magit-20240808.1852/magit.elc b/emacs/elpa/magit-20240811.1419/magit.elc Binary files differ. diff --git a/emacs/elpa/magit-20240808.1852/magit.info b/emacs/elpa/magit-20240811.1419/magit.info diff --git a/emacs/elpa/magit-section-20240808.1852/magit-section-pkg.el b/emacs/elpa/magit-section-20240808.1852/magit-section-pkg.el @@ -1,14 +0,0 @@ -(define-package "magit-section" "20240808.1852" "Sections for read-only buffers." - '((emacs "26.1") - (compat "30.0.0.0") - (dash "2.19.1")) - :commit "020aca7c9c4154dbc4a72acbd56165ecccea1bf1" :authors - '(("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev")) - :maintainer - '("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") - :keywords - '("tools") - :url "https://github.com/magit/magit") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/magit-section-20240808.1852/dir b/emacs/elpa/magit-section-20240811.1419/dir diff --git a/emacs/elpa/magit-section-20240808.1852/magit-section-autoloads.el b/emacs/elpa/magit-section-20240811.1419/magit-section-autoloads.el diff --git a/emacs/elpa/magit-section-20240811.1419/magit-section-pkg.el b/emacs/elpa/magit-section-20240811.1419/magit-section-pkg.el @@ -0,0 +1,14 @@ +(define-package "magit-section" "20240811.1419" "Sections for read-only buffers." + '((emacs "26.1") + (compat "30.0.0.0") + (dash "20240510")) + :commit "a2739d7db1fdf19b95f36f6ddd15b0c1f523bd26" :authors + '(("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev")) + :maintainer + '("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev") + :keywords + '("tools") + :url "https://github.com/magit/magit") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs/elpa/magit-section-20240808.1852/magit-section.el b/emacs/elpa/magit-section-20240811.1419/magit-section.el diff --git a/emacs/elpa/magit-section-20240808.1852/magit-section.elc b/emacs/elpa/magit-section-20240811.1419/magit-section.elc Binary files differ. diff --git a/emacs/elpa/magit-section-20240808.1852/magit-section.info b/emacs/elpa/magit-section-20240811.1419/magit-section.info diff --git a/emacs/elpa/marginalia-20240726.2129/marginalia-pkg.el b/emacs/elpa/marginalia-20240726.2129/marginalia-pkg.el @@ -1,17 +0,0 @@ -(define-package "marginalia" "20240726.2129" "Enrich existing commands with completion annotations" - '((emacs "27.1") - (compat "30")) - :commit "7a7f3363d042d1bf43ae697f4401638ed18230a5" :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") - :keywords - '("docs" "help" "matching" "completion") - :url "https://github.com/minad/marginalia") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/emacs/elpa/marginalia-20240726.2129/marginalia.el b/emacs/elpa/marginalia-20240726.2129/marginalia.el @@ -1,1365 +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.7 -;; Package-Requires: ((emacs "27.1") (compat "30")) -;; 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 - -(defalias 'marginalia--orig-completion-metadata-get - (symbol-function (compat-function completion-metadata-get)) - "Original `completion-metadata-get' function.") - -(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, dispatching to the appropriate annotator." - (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. Bypass - ;; our `marginalia--completion-metadata-get' advice. - (when-let (annotate (marginalia--orig-completion-metadata-get - marginalia--metadata 'annotation-function)) - (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 - ((guard (static-if (< emacs-major-version 30) - (and (vectorp val) (ignore-errors (abbrev-table-p val))) - (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." - ;; Bypass our `marginalia--completion-metadata-get' advice. - (when-let (cat (marginalia--orig-completion-metadata-get marginalia--metadata 'category)) - ;; 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 (compat-function completion-metadata-get) :before-until #'marginalia--completion-metadata-get) - (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 (compat-function completion-metadata-get) #'marginalia--completion-metadata-get) - (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))) - (md (completion-metadata (buffer-substring-no-properties end (+ end pt)) - minibuffer-completion-table - minibuffer-completion-predicate)) - (cat (or (completion-metadata-get md '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. Bypass the - ;; `marginalia--completion-metadata-get' advice. - (when (and (eq (cadr ann) 'builtin) - (not (marginalia--orig-completion-metadata-get md 'annotation-function)) - (not (marginalia--orig-completion-metadata-get md '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-20240726.2129/marginalia.elc b/emacs/elpa/marginalia-20240726.2129/marginalia.elc Binary files differ. diff --git a/emacs/elpa/marginalia-20240726.2129/marginalia-autoloads.el b/emacs/elpa/marginalia-20240813.701/marginalia-autoloads.el diff --git a/emacs/elpa/marginalia-20240813.701/marginalia-pkg.el b/emacs/elpa/marginalia-20240813.701/marginalia-pkg.el @@ -0,0 +1,17 @@ +(define-package "marginalia" "20240813.701" "Enrich existing commands with completion annotations" + '((emacs "27.1") + (compat "30")) + :commit "c34fdacce64168cb20d710a87e66cc9d1f795a82" :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") + :keywords + '("docs" "help" "matching" "completion") + :url "https://github.com/minad/marginalia") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/emacs/elpa/marginalia-20240813.701/marginalia.el b/emacs/elpa/marginalia-20240813.701/marginalia.el @@ -0,0 +1,1365 @@ +;;; 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.7 +;; Package-Requires: ((emacs "27.1") (compat "30")) +;; 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\\|advice\\) 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 + +(defalias 'marginalia--orig-completion-metadata-get + (symbol-function (compat-function completion-metadata-get)) + "Original `completion-metadata-get' function.") + +(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, dispatching to the appropriate annotator." + (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. Bypass + ;; our `marginalia--completion-metadata-get' advice. + (when-let (annotate (marginalia--orig-completion-metadata-get + marginalia--metadata 'annotation-function)) + (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 + ((guard (static-if (< emacs-major-version 30) + (and (vectorp val) (ignore-errors (abbrev-table-p val))) + (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." + ;; Bypass our `marginalia--completion-metadata-get' advice. + (when-let (cat (marginalia--orig-completion-metadata-get marginalia--metadata 'category)) + ;; 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 (compat-function completion-metadata-get) :before-until #'marginalia--completion-metadata-get) + (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 (compat-function completion-metadata-get) #'marginalia--completion-metadata-get) + (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))) + (md (completion-metadata (buffer-substring-no-properties end (+ end pt)) + minibuffer-completion-table + minibuffer-completion-predicate)) + (cat (or (completion-metadata-get md '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. Bypass the + ;; `marginalia--completion-metadata-get' advice. + (when (and (eq (cadr ann) 'builtin) + (not (marginalia--orig-completion-metadata-get md 'annotation-function)) + (not (marginalia--orig-completion-metadata-get md '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-20240813.701/marginalia.elc b/emacs/elpa/marginalia-20240813.701/marginalia.elc Binary files differ. diff --git a/emacs/elpa/pdf-tools-20240429.407/epdfinfo b/emacs/elpa/pdf-tools-20240429.407/epdfinfo Binary files differ.